Migration to Rust removal of Azure.

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-10-06 10:29:14 -03:00
parent 88c409fb6b
commit 1bb97ebee9
261 changed files with 17 additions and 694471 deletions

View file

@ -1,10 +1,10 @@
name: GBCI
run: git config --global http.sslVerify false
on:
push:
branches: ['main']
branches: ["main"]
pull_request:
branches: ['main']
branches: ["main"]
jobs:
build:
@ -16,30 +16,21 @@ jobs:
- uses: actions/checkout@v4
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '20'
# cache: 'npm'
- name: Install Rust
uses: msrd0/rust-toolchain@v1
with:
toolchain: stable
- name: Copy files to deployment location
- name: Run build
run: |
echo "[General Bots Deployer] Copying files to deploy location..."
sudo rm -rf /opt/gbo/bin/bot/botserver/dist
sudo rm -f /opt/gbo/bin/bot/botserver/package-lock.json
sudo cp -r ./* /opt/gbo/bin/bot/botserver
sudo cp /opt/gbo/bin/system/.env .
cargo build --locked
- name: Building BotServer
- name: Deploy binary and restart
run: |
echo "[General Bots Deployer] Building BotServer..."
lxc exec bot:pragmatismo-system -- systemctl stop system
rm -rf /opt/gbo/bin/bot/botserver/node_modules
cd /opt/gbo/bin/bot/botserver
sudo npm i
npm run build-server
npm run build-gbui
sudo cp ./target/debug/gbserver /opt/gbo/bin/system
sudo chmod +x /opt/gbo/bin/system/gbserver
- name: Restart Bots Deployer
run: |
echo "[General Bots Deployer] Restarting..."
lxc restart pragmatismo-bot
lxc exec bot:pragmatismo-system -- systemctl start system

28
.gitignore vendored
View file

@ -1,28 +1,4 @@
/.coveralls.yml
/.env
/.npmrc
/.nyc_output
/coverage
/dist
/docs
/guaribas.sqlite
/node_modules
/packages/default.gbui/build
/packages/default.gbui/.env
/packages/default.gbui/node_modules
/packages/default.gbui/package-lock.json
/packages/default.gbui/yarn-lock.json
/work
*.vbs.compiled
*.vbs.js
*.vbs.ts
target
.env
*.env
.vscode/launch.json
.wwebjs_auth
yarn-error.log
yarn-lock.json
logo.svg
screenshot.png
data.db
.wwebjs_cache
work

View file

@ -1,9 +0,0 @@
{
"extends": [
"development"
],
"hints": {
"typescript-config/strict": "off",
"typescript-config/consistent-casing": "off"
}
}

View file

@ -1,15 +0,0 @@
# This file must be a copy of .gitignore except for the WILLSHIP commented lines below.
/.coveralls.yml
/.env
/.npmrc
# WILLSHIP /.nyc_output
/coverage
# WILLSHIP /dist
/guaribas.log
/guaribas.sqlite
/node_modules
# WILLSHIP /packages/default.gbui/build
/packages/default.gbui/.env
/packages/default.gbui/node_modules
/tmp
/work

View file

@ -1,8 +0,0 @@
{
"trailingComma": "none",
"tabWidth": 2,
"printWidth": 120,
"arrowParens": "avoid",
"semi": true,
"singleQuote": true
}

View file

@ -1,45 +0,0 @@
import { expect, test } from 'vitest';
import { GBServer } from './src/app';
import { RootData } from './src/RootData';
import { GBMinInstance } from 'botlib-legacy';
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

@ -1,148 +0,0 @@
# 2.0.0
``` SQL
ALTER TABLE dbo.GuaribasUser ADD
agentSystemId nvarchar(16) NULL,
agentMode nvarchar(16) NULL,
agentContacted datetime NULL
GO
ALTER TABLE [dbo].[GuaribasUser] DROP COLUMN [phone]
GO
ALTER TABLE [dbo].[GuaribasUser] DROP COLUMN [internalAddress]
GO
ALTER TABLE [dbo].[GuaribasUser] DROP COLUMN [currentBotId]
GO
ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [authenticatorClientId]
GO
ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [authenticatorClientSecret]
GO
ALTER TABLE dbo.GuaribasUser ADD
locale nvarchar(5) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD
translatorKey nvarchar(64) NULL
translatorEndpoint nvarchar(64) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD
activationCode nvarchar(16) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD
params nvarchar(4000) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD
state nvarchar(16) NULL
GO
UPDATE dbo.GuaribasInstance SET state= 'active'
# 2.0.3
``` SQL
ALTER TABLE dbo.GuaribasPackage ADD
params custom(512) NULL
GO
```
# 2.0.56
ALTER TABLE dbo.GuaribasUser ADD
hearOnDialog nvarchar(64) NULL
GO
ALTER TABLE dbo.GuaribasConversation ADD
instanceId int,
feedback nvarchar(512) NULL
GO
ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [translatorendpoint]
GO
ALTER TABLE dbo.GuaribasInstance ADD
translatorEndpoint nvarchar(128) NULL
GO
# 2.0.108
ALTER TABLE [dbo].[GuaribasInstance] DROP COLUMN [agentSystemId]
GO
ALTER TABLE dbo.GuaribasUser ADD
agentSystemId nvarchar(255) NULL,
GO
# 2.0.115
ALTER TABLE dbo.GuaribasQuestion ADD
skipIndex bit NULL
GO
# 2.0.116 >
ALTER TABLE dbo.GuaribasInstance ADD
googleBotKey nvarchar(255) NULL,
googleChatApiKey nvarchar(255) NULL,
googleChatSubscriptionName nvarchar(255) NULL,
googleClientEmail nvarchar(255) NULL,
googlePrivateKey nvarchar(4000) NULL,
googleProjectId nvarchar(255) NULL
GO
# 2.0.119
ALTER TABLE dbo.GuaribasInstance ADD
facebookWorkplaceVerifyToken nvarchar(255) NULL,
facebookWorkplaceAppSecret nvarchar(255) NULL,
facebookWorkplaceAccessToken nvarchar(512) NULL
GO
# 2.0.140
/****** Object: Table [dbo].[GuaribasSchedule] Script Date: 25/08/2021 03:53:15 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[GuaribasSchedule]
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](255) NULL,
[schedule] [nvarchar](255) NULL,
[instanceId] [int] NULL,
[createdAt] [datetimeoffset](7) NULL,
[updatedAt] [datetimeoffset](7) NULL
GO
ALTER TABLE dbo.GuaribasInstance ADD botKey nvarchar(64) NULL;
# 2.3.9
GO
ALTER TABLE dbo.GuaribasUser ADD
params nvarchar(4000) NULL
GO

View file

@ -1,79 +0,0 @@
# default.gbui
https://github.com/microsoft/BotFramework-WebChat/pull/4524
warning botframework-directlinejs > core-js@3.15.2: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-component > @emotion/css > @emotion/babel-plugin > @babel/plugin-syntax-jsx@7.18.6" has unmet peer dependency "@babel/core@^7.0.0-0".
warning botframework-webchat > botframework-webchat-component > @emotion/css > @emotion/babel-plugin@11.10.5" has unmet peer dependency "@babel/core@^7.0.0".
warning botframework-webchat > botframework-webchat-component > react-film > core-js@3.12.1: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-component > react-scroll-to-bottom > core-js@3.18.3: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning botframework-webchat > botframework-webchat-core > redux-devtools-extension@2.13.9: Package moved to @redux-devtools/extension.
warning botframework-webchat > microsoft-cognitiveservices-speech-sdk > asn1.js-rfc2560@5.0.1" has unmet peer dependency "asn1.js@^5.0.0".
warning botframework-webchat > web-speech-cognitive-services@7.1.2" has incorrect peer dependency "microsoft-cognitiveservices-speech-sdk@~1.17.0".
https://github.com/microsoft/powerbi-client-react
warning react-powerbi@0.9.1" has incorrect peer dependency "react@^16.8.0".
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo > stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
warning react-scripts > @svgr/webpack > @svgr/plugin-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x.
warning react-scripts > css-minimizer-webpack-plugin > cssnano > cssnano-preset-default > postcss-svgo > svgo > stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
warning react-scripts > eslint-config-react-app > @typescript-eslint/eslint-plugin > tsutils@3.21.0" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta".
warning react-scripts > eslint-config-react-app > eslint-plugin-flowtype@8.0.3" has unmet peer dependency "@babel/plugin-syntax-flow@^7.14.5".
warning react-scripts > eslint-config-react-app > eslint-plugin-flowtype@8.0.3" has unmet peer dependency "@babel/plugin-transform-react-jsx@^7.14.9".
warning react-scripts > jest > @jest/core > jest-config > jest-environment-jsdom > jsdom > w3c-hr-time@1.0.2: Use your platform's native performance.now() and performance.timeOrigin.
warning react-scripts > react-dev-utils > fork-ts-checker-webpack-plugin@6.5.2" has unmet peer dependency "typescript@>= 2.7".
# BotServer
docxtemplater is not working in more modern versions. Stay with 3.9.7.
nodejs/node-gyp#2756
warning npm > node-gyp > make-fetch-happen > cacache > @npmcli/move-file@2.0.1: This functionality has been moved to @npmcli/fs
vasyas/typescript-rest-rpc#20
warning typescript-rest-rpc > ts-morph > globby > fast-glob > micromatch > snapdragon > source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
#279
warning tslint@6.1.3: TSLint has been deprecated in favor of ESLint. Please see palantir/tslint#4534 for more information.
AlaSQL/alasql#1541
warning alasql > request@2.88.2: request has been deprecated, see request/request#3142
#281
warning c3-chart-maker > data-forge > promised-mongo > mongodb-core > bson@0.4.23: Fixed a critical issue with BSON serialization documented in CVE-2019-2391, see https://bit.ly/2KcpXdo for more details
#280
warning swagger-client > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
bahmutov/ggit#157
warning ban-sensitive-files > ggit > debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (debug-js/debug#797)
#283
warning nexmo > uuid@2.0.3: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
https://github.com/microsoft/botbuilder-js/issues/4370
warning botbuilder-ai > @azure/cognitiveservices-luis-runtime > @azure/ms-rest-js > uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
https://github.com/Azure/azure-sdk-for-node/issues/5221
warning ms-rest-azure > request@2.88.2: request has been deprecated, see request/request#3142
https://github.com/MontassarLaribi/ssr-for-bots/issues/1
warning ssr-for-bots > tslint@6.1.3: TSLint has been deprecated in favor of ESLint. Please see palantir/tslint#4534 for more information.
https://github.com/vasyas/typescript-rest-rpc/issues/20
warning typescript-rest-rpc > ts-morph > globby > fast-glob > micromatch > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
https://github.com/ash-developer/winston-logs-display/issues/8
warning winston-logs-display > jade > transformers@2.1.0: Deprecated, use jstransformer
https://github.com/softwarescales/git-issues/issues/29
warning git-issues > request@2.88.2: request has been deprecated, see request/request#3142
https://github.com/GeneralBots/BotServer/issues/284
warning license-checker > read-installed > readdir-scoped-modules@1.1.0: This functionality has been moved to @npmcli/fs
https://github.com/semantic-release/semantic-release/issues/1260
warning semantic-release > @semantic-release/npm > npm > readdir-scoped-modules@1.1.0: This functionality has been moved to @npmcli/fs
https://github.com/GeneralBots/BotServer/issues/277
warning travis-deploy-once@3.3.0: We recommend to use Travis Build Stages instead

View file

@ -1,14 +0,0 @@
{
"openapi": "3.0.0",
"info": {
"title": "General Bots API",
"description": "General Bots API description in Swagger format",
"version": "1.0"
},
"servers": [
{
"url": "https://generalbots.online/api",
"description": "General Bots Online"
}
]
}

Binary file not shown.

Binary file not shown.

View file

@ -1,57 +0,0 @@
#!/usr/bin/env node
process.stdout.write(`General Bots 5 VM: node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
import fs from 'fs/promises';
import os from 'node:os';
import path from 'path';
import { exec } from 'child_process';
import {GBUtil} from './dist/src/util.js'
// Displays version of Node JS being used at runtime and others attributes.
console.log(`\nLoading General Bots VM...`);
var __dirname = process.env.PWD || process.cwd();
try {
var run = async () => {
import('./dist/src/app.js').then(async (gb)=> {
await gb.GBServer.run()
});
};
var processDist = async () => {
if (!await GBUtil.exists('dist')) {
console.log(`\n`);
console.log(`General Bots: Compiling...`);
exec(path.join(__dirname, 'node_modules/.bin/tsc'), async (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
await run();
});
} else {
await run();
}
};
// Installing modules if it has not been done yet.
if (!await GBUtil.exists('node_modules')) {
console.log(`\n`);
console.log(`General Bots: Installing modules for the first time, please wait...`);
exec('npm install', async (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
await processDist();
});
} else {
await processDist();
}
} catch (e) {
console.log(e);
}

View file

@ -1,150 +0,0 @@
@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off
:: ----------------------
:: General Bots deployment.
:: -------------
:: Verify node.js installed
where node 2>nul >nul
IF %ERRORLEVEL% NEQ 0 (
echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
goto error
)
:: Setup
:: -----
setlocal enabledelayedexpansion
SET ARTIFACTS=%~dp0%..\artifacts
IF NOT DEFINED DEPLOYMENT_SOURCE (
SET DEPLOYMENT_SOURCE=%~dp0%.
)
IF NOT DEFINED DEPLOYMENT_TARGET (
SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
)
IF NOT DEFINED NEXT_MANIFEST_PATH (
SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest
IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
)
)
IF NOT DEFINED KUDU_SYNC_CMD (
:: Install kudu sync
echo Installing Kudu Sync
call npm install kudusync -g --silent
IF !ERRORLEVEL! NEQ 0 goto error
:: Locally just running "kuduSync" would also work
SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
)
goto Deployment
:: Utility Functions
:: -----------------
:SelectNodeVersion
IF DEFINED KUDU_SELECT_NODE_VERSION_CMD (
:: The following are done only on Windows Azure Websites environment
call %KUDU_SELECT_NODE_VERSION_CMD% "%DEPLOYMENT_SOURCE%" "%DEPLOYMENT_TARGET%" "%DEPLOYMENT_TEMP%"
IF !ERRORLEVEL! NEQ 0 goto error
IF EXIST "%DEPLOYMENT_TEMP%\__nodeVersion.tmp" (
SET /p NODE_EXE=<"%DEPLOYMENT_TEMP%\__nodeVersion.tmp"
IF !ERRORLEVEL! NEQ 0 goto error
)
IF EXIST "%DEPLOYMENT_TEMP%\__npmVersion.tmp" (
SET /p NPM_JS_PATH=<"%DEPLOYMENT_TEMP%\__npmVersion.tmp"
IF !ERRORLEVEL! NEQ 0 goto error
)
IF NOT DEFINED NODE_EXE (
SET NODE_EXE=node
)
SET NPM_CMD="!NODE_EXE!" "!NPM_JS_PATH!"
) ELSE (
SET NPM_CMD=npm
SET NODE_EXE=node
)
goto :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------
:Deployment
echo Handling node.js deployment.
:: 1. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
IF !ERRORLEVEL! NEQ 0 goto error
)
:: 2. Select node version
call :SelectNodeVersion
:: 3. Install npm packages
IF EXIST "%DEPLOYMENT_TARGET%\package.json" (
pushd "%DEPLOYMENT_TARGET%"
echo [General Bots Deployer] Installing packages for server...
call :ExecuteCmd !NPM_CMD! install --production
IF !ERRORLEVEL! NEQ 0 goto error
popd
)
:: 3.1 Install npm packages on UI
IF EXIST "%DEPLOYMENT_TARGET%\deploy\default.gbui\package.json" (
call :ExecuteCmd !NPM_CMD! config set scripts-prepend-node-path true
pushd "%DEPLOYMENT_TARGET%\deploy\default.gbui"
echo [General Bots Deployer] Installing packages for default.gbui...
call :ExecuteCmd !NPM_CMD! install
echo [General Bots Deployer] Building default.gbui...
call :ExecuteCmd !NPM_CMD! run build
IF !ERRORLEVEL! NEQ 0 goto error
RMDIR /s /q "%DEPLOYMENT_TARGET%\deploy\default.gbui\node_modules"
popd
)
:: 4. Install TypeScript
echo [General Bots Deployer] Transpiling...
call :ExecuteCmd node %DEPLOYMENT_TARGET%\node_modules\typescript\bin\tsc -v
call :ExecuteCmd node %DEPLOYMENT_TARGET%\node_modules\typescript\bin\tsc -p "%DEPLOYMENT_TARGET%"
echo [General Bots Deployer] Deployment Finished.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto end
:: Execute command routine that will echo out when error
:ExecuteCmd
setlocal
set _CMD_=%*
call %_CMD_%
if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
exit /b %ERRORLEVEL%
:error
endlocal
echo An error has occurred during web site deployment.
call :exitSetErrorLevel
call :exitFromFunction 2>nul
:exitSetErrorLevel
exit /b 1
:exitFromFunction
()
:end
endlocal
echo Finished successfully.

View file

@ -1,96 +0,0 @@
#!/bin/bash
# ------------------------
# General Bots deployment.
# ------------------------
# Helpers
# -------
exitWithMessageOnError () {
if [ ! $? -eq 0 ]; then
echo "[General Bots Deployer]An error has occurred during web site deployment."
echo $1
exit 1
fi
}
# Prerequisites
# -------------
# Verify node.js installed
hash node 2>/dev/null
exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment."
# Setup
# -----
SCRIPT_DIR="${BASH_SOURCE[0]%\\*}"
SCRIPT_DIR="${SCRIPT_DIR%/*}"
ARTIFACTS=$SCRIPT_DIR/../artifacts
KUDU_SYNC_CMD=${KUDU_SYNC_CMD//\"}
if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then
DEPLOYMENT_SOURCE=$SCRIPT_DIR
fi
if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then
NEXT_MANIFEST_PATH=$ARTIFACTS/manifest
if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then
PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH
fi
fi
if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then
DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot
else
KUDU_SERVICE=true
fi
if [[ ! -n "$KUDU_SYNC_CMD" ]]; then
# Install kudu sync
echo Installing Kudu Sync
npm install kudusync -g --silent
exitWithMessageOnError "npm failed"
if [[ ! -n "$KUDU_SERVICE" ]]; then
# In case we are running locally this is the correct location of kuduSync
KUDU_SYNC_CMD=kuduSync
else
# In case we are running on kudu service this is the correct location of kuduSync
KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync
fi
fi
##################################################################################################################################
# Deployment
# ----------
# 1. Install npm packages
if [ -e "$DEPLOYMENT_SOURCE/package.json" ]; then
echo "[General Bots Deployer] Running npm install..."
cd "$DEPLOYMENT_SOURCE"
eval npm install
echo "[General Bots Deployer] OK."
exitWithMessageOnError "npm failed"
cd - > /dev/null
fi
# 2. Install TypeScript
echo "[General Bots Deployer] Transpiling..."
eval ./node_modules/typescript/bin/tsc -v
eval ./node_modules/typescript/bin/tsc -p "$DEPLOYMENT_SOURCE"
echo "[General Bots Deployer] OK."
# 4. KuduSync
if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then
"$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh"
exitWithMessageOnError "Kudu Sync failed"
fi
##################################################################################################################################
echo "[General Bots Deployer] Finished successfully."

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
theme: jekyll-theme-minimal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 B

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
window.searchData = {"kinds":{"128":"Class","512":"Constructor","1024":"Property","2048":"Method"},"rows":[{"id":0,"kind":128,"name":"RootData","url":"classes/rootdata.html","classes":"tsd-kind-class"},{"id":1,"kind":512,"name":"constructor","url":"classes/rootdata.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"RootData"},{"id":2,"kind":1024,"name":"publicAddress","url":"classes/rootdata.html#publicaddress","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":3,"kind":1024,"name":"server","url":"classes/rootdata.html#server","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":4,"kind":1024,"name":"sysPackages","url":"classes/rootdata.html#syspackages","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":5,"kind":1024,"name":"appPackages","url":"classes/rootdata.html#apppackages","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":6,"kind":1024,"name":"minService","url":"classes/rootdata.html#minservice","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":7,"kind":1024,"name":"bootInstance","url":"classes/rootdata.html#bootinstance","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":8,"kind":1024,"name":"minInstances","url":"classes/rootdata.html#mininstances","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":9,"kind":1024,"name":"minBoot","url":"classes/rootdata.html#minboot","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":10,"kind":1024,"name":"wwwroot","url":"classes/rootdata.html#wwwroot","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":11,"kind":1024,"name":"entryPointDialog","url":"classes/rootdata.html#entrypointdialog","classes":"tsd-kind-property tsd-parent-kind-class","parent":"RootData"},{"id":12,"kind":128,"name":"GBServer","url":"classes/gbserver.html","classes":"tsd-kind-class"},{"id":13,"kind":1024,"name":"globals","url":"classes/gbserver.html#globals","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-static","parent":"GBServer"},{"id":14,"kind":2048,"name":"run","url":"classes/gbserver.html#run","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-static","parent":"GBServer"},{"id":15,"kind":512,"name":"constructor","url":"classes/gbserver.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"GBServer"}],"index":{"version":"2.3.9","fields":["name","parent"],"fieldVectors":[["name/0",[0,3.075]],["parent/0",[]],["name/1",[1,19.169]],["parent/1",[0,0.291]],["name/2",[2,24.277]],["parent/2",[0,0.291]],["name/3",[3,24.277]],["parent/3",[0,0.291]],["name/4",[4,24.277]],["parent/4",[0,0.291]],["name/5",[5,24.277]],["parent/5",[0,0.291]],["name/6",[6,24.277]],["parent/6",[0,0.291]],["name/7",[7,24.277]],["parent/7",[0,0.291]],["name/8",[8,24.277]],["parent/8",[0,0.291]],["name/9",[9,24.277]],["parent/9",[0,0.291]],["name/10",[10,24.277]],["parent/10",[0,0.291]],["name/11",[11,24.277]],["parent/11",[0,0.291]],["name/12",[12,13.291]],["parent/12",[]],["name/13",[13,24.277]],["parent/13",[12,1.256]],["name/14",[14,24.277]],["parent/14",[12,1.256]],["name/15",[1,19.169]],["parent/15",[12,1.256]]],"invertedIndex":[["apppackages",{"_index":5,"name":{"5":{}},"parent":{}}],["bootinstance",{"_index":7,"name":{"7":{}},"parent":{}}],["constructor",{"_index":1,"name":{"1":{},"15":{}},"parent":{}}],["entrypointdialog",{"_index":11,"name":{"11":{}},"parent":{}}],["gbserver",{"_index":12,"name":{"12":{}},"parent":{"13":{},"14":{},"15":{}}}],["globals",{"_index":13,"name":{"13":{}},"parent":{}}],["minboot",{"_index":9,"name":{"9":{}},"parent":{}}],["mininstances",{"_index":8,"name":{"8":{}},"parent":{}}],["minservice",{"_index":6,"name":{"6":{}},"parent":{}}],["publicaddress",{"_index":2,"name":{"2":{}},"parent":{}}],["rootdata",{"_index":0,"name":{"0":{}},"parent":{"1":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{},"10":{},"11":{}}}],["run",{"_index":14,"name":{"14":{}},"parent":{}}],["server",{"_index":3,"name":{"3":{}},"parent":{}}],["syspackages",{"_index":4,"name":{"4":{}},"parent":{}}],["wwwroot",{"_index":10,"name":{"10":{}},"parent":{}}]],"pipeline":[]}}

View file

@ -1,213 +0,0 @@
<!doctype html>
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>General Bots Open Core</title>
<meta name="description" content="Documentation for General Bots Open Core">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
<script async src="assets/js/search.js" id="search-script"></script>
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="assets/js/search.json" data-base=".">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="index.html" class="title">General Bots Open Core</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
<input type="checkbox" id="tsd-filter-externals" checked />
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<h1>General Bots Open Core</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<div class="tsd-panel tsd-typography">
<table>
<thead>
<tr>
<th>Area</th>
<th>Status</th>
</tr>
</thead>
<tbody><tr>
<td>Releases</td>
<td><a href="https://www.npmjs.com/package/botserver/"><img src="https://img.shields.io/npm/dt/botserver.svg?logo=npm&label=botserver" alt="General Bots"></a> <a href="https://www.npmjs.com/package/botlib/"><img src="https://img.shields.io/npm/dt/botlib.svg?logo=npm&label=botlib" alt=".gbapp lib"></a> <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-release"></a></td>
</tr>
<tr>
<td>Community</td>
<td><a href="https://stackoverflow.com/questions/tagged/generalbots"><img src="https://img.shields.io/stackexchange/stackoverflow/t/generalbots.svg" alt="StackExchange"></a> <a href="https://badges.frapsoft.com"><img src="https://badges.frapsoft.com/os/v2/open-source.svg" alt="Open-source"></a> <a href="http://makeapullrequest.com"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" alt="PRs Welcome"></a> <a href="https://github.com/GeneralBots/BotServer/blob/master/LICENSE.txt"><img src="https://img.shields.io/badge/license-AGPL-blue.svg" alt="License"></a></td>
</tr>
<tr>
<td>Management</td>
<td><a href="https://gitHub.com/GeneralBots/BotServer/graphs/commit-activity"><img src="https://img.shields.io/badge/Maintained%3F-yes-green.svg" alt="Maintenance"></a></td>
</tr>
<tr>
<td>Security</td>
<td><a href="https://snyk.io/test/github/GeneralBots/BotServer"><img src="https://snyk.io/test/github/GeneralBots/BotServer/badge.svg" alt="Known Vulnerabilities"></a></td>
</tr>
<tr>
<td>Building &amp; Quality</td>
<td><a href="https://travis-ci.com/GeneralBots/BotServer"><img src="https://travis-ci.com/GeneralBots/BotServer.svg?branch=master" alt="Build Status"></a> <a href="https://coveralls.io/github/GeneralBots/BotServer"><img src="https://coveralls.io/repos/github/GeneralBots/BotServer/badge.svg" alt="Coverage Status"></a> <a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" alt="code style: prettier"></a></td>
</tr>
<tr>
<td>Packaging</td>
<td><a href="https://badge.fury.io"><img src="https://badge.fury.io/js/botserver.svg" alt="forthebadge"></a> <a href="https://github.com/GeneralBots/BotServer/releases/latest"><img src="https://camo.githubusercontent.com/0150c0f148d50fe9750ebc5d313581da699a8c50/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7a69702d646f776e6c6f61642d626c75652e737667" alt="ZipFile"></a> <a href="https://david-dm.org"><img src="https://david-dm.org/GeneralBots/botserver.svg" alt="Dependencies"></a> <a href="http://commitizen.github.io/cz-cli/"><img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen friendly"></a></td>
</tr>
<tr>
<td>Samples</td>
<td><a href="https://github.com/GeneralBots/BotServer/tree/master/packages/default.gbdialog">VBA</a> or <a href="https://github.com/GeneralBots/AzureADPasswordReset.gbapp"><img src="https://badges.frapsoft.com/typescript/code/typescript.svg?v=101" alt="TypeScript"></a></td>
</tr>
<tr>
<td><a href="https://github.com/lpicanco/docker-botserver">Docker Image</a></td>
<td><img src="https://img.shields.io/docker/automated/lpicanco/botserver.svg" alt="Docker Automated build"> <img src="https://img.shields.io/docker/build/lpicanco/botserver.svg" alt="Docker Build Status"> <img src="https://img.shields.io/microbadger/image-size/lpicanco/botserver.svg" alt="MicroBadger Size"> <img src="https://img.shields.io/microbadger/layers/lpicanco/botserver.svg" alt="MicroBadger Layers"> <img src="https://img.shields.io/docker/pulls/lpicanco/botserver.svg" alt="Docker Pulls"> <br/> <em>Provided by <a href="https://github.com/lpicanco/docker-botserver">@lpicanco</a></em></td>
</tr>
</tbody></table>
<a href="#general-bots" id="general-bots" style="color: inherit; text-decoration: none;">
<h2>General Bots</h2>
</a>
<p><img src="https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/logo.png" alt="General Bot Logo"></p>
<p>General Bot is a strongly typed package based chat bot server focused in convention over configuration and code-less approaches, which brings software packages and application server concepts to help parallel bot development.</p>
<a href="#what-is-a-bot-server" id="what-is-a-bot-server" style="color: inherit; text-decoration: none;">
<h2>What is a Bot Server?</h2>
</a>
<p>Bot Server accelerates the process of developing a bot. It provisions all code
base, resources and deployment to the cloud, and gives you templates you can
choose from whenever you need a new bot. The server has a database and service
backend allowing you to further modify your bot package directly by downloading
a zip file, editing and uploading it back to the server (deploying process) with
no code. The Bot Server also provides a framework to develop bot packages in a more
advanced fashion writing custom code in editors like Visual Studio Code, Atom or Brackets.</p>
<p>Everyone can create bots by just copying and pasting some files and using their
favorite tools from Office (or any text editor) or Photoshop (or any image
editor). BASIC can be used to build custom dialogs so Bot can be extended just like VBA for Excel (currently in alpha).</p>
<p><img src="https://raw.githubusercontent.com/GeneralBots/BotBook/master/images/general-bots-reference-architecture.png" alt="General Bot Reference Architecture"></p>
<a href="#samples" id="samples" style="color: inherit; text-decoration: none;">
<h2>Samples</h2>
</a>
<p>Several samples, including a Bot for AD Password Reset, are avaiable on the <a href="https://github.com/GeneralBots">repository list</a>.</p>
<a href="#guide" id="guide" style="color: inherit; text-decoration: none;">
<h2>Guide</h2>
</a>
<p><a href="https://github.com/GeneralBots/BotBook/tree/master/book">Read the General Bots BotBook Guide</a>.</p>
<a href="#videos" id="videos" style="color: inherit; text-decoration: none;">
<h1>Videos</h1>
</a>
<p>Now with the General Bots server you can press F5 on Visual Studio to get a bot factory on your environment* published on November 10th, 2018.</p>
<p><a href="https://www.youtube.com/watch?v=AfKTwljoMOs"><img src="https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/docs/images/video-01-thumb.jpg" alt="General Bot Video"></a></p>
<p>See how easy is to use &#39;hear&#39; and &#39;talk&#39; to build Microsoft BOT Framework v4 logic with plain BASIC * published on December 3rd, 2018.</p>
<p><a href="https://www.youtube.com/watch?v=yX1sF9n9628"><img src="https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/docs/images/video-02-thumb.jpg" alt="See how easy is to use &#39;hear&#39; and &#39;talk&#39; to build Microsoft BOT Framework v4 logic with plain BASIC"></a></p>
<a href="#contributing" id="contributing" style="color: inherit; text-decoration: none;">
<h1>Contributing</h1>
</a>
<p>This project welcomes contributions and suggestions.
See our <a href="https://github.com/pragmatismo-io/BotServer/blob/master/CONTRIBUTING.md">Contribution Guidelines</a> for more details.</p>
<a href="#reporting-security-issues" id="reporting-security-issues" style="color: inherit; text-decoration: none;">
<h1>Reporting Security Issues</h1>
</a>
<p>Security issues and bugs should be reported privately, via email, to the Pragmatismo.io Security
team at <a href="mailto:security@pragmatismo.io">security@pragmatismo.io</a>. You should
receive a response within 24 hours. If for some reason you do not, please follow up via
email to ensure we received your original message. </p>
<a href="#license-amp-warranty" id="license-amp-warranty" style="color: inherit; text-decoration: none;">
<h1>License &amp; Warranty</h1>
</a>
<p>General Bot Copyright (c) Pragmatismo.io. All rights reserved.
Licensed under the AGPL-3.0. </p>
<p>According to our dual licensing model, this program can be used either
under the terms of the GNU Affero General Public License, version 3,
or under a proprietary license. </p>
<p>The texts of the GNU Affero General Public License with an additional
permission and of our proprietary license can be found at and
in the LICENSE file you have received along with this program.</p>
<p>This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.</p>
<p>&quot;General Bot&quot; is a registered trademark of Pragmatismo.io.
The licensing of the program under the AGPLv3 does not imply a
trademark license. Therefore any rights, title and interest in
our trademarks remain entirely with us.</p>
<p><a href="https://stackoverflow.com/questions/ask?tags=generalbots">:speech_balloon: Ask a question</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://github.com/GeneralBots/BotBook">:book: Read the Docs</a></p>
<p>General Bots Code Name is <a href="https://en.wikipedia.org/wiki/Guaribas">Guaribas</a>, the name of a city in Brazil, state of Piaui.
<a href="http://www.robertounger.com/en/">Roberto Mangabeira Unger</a>: &quot;No one should have to do work that can be done by a machine&quot;.</p>
</div>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<nav class="tsd-navigation primary">
<ul>
<li class=" ">
<a href="modules.html">Exports</a>
</li>
</ul>
</nav>
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
<li class=" tsd-kind-class">
<a href="classes/gbserver.html" class="tsd-kind-icon">GBServer</a>
</li>
<li class=" tsd-kind-class">
<a href="classes/rootdata.html" class="tsd-kind-icon">Root<wbr>Data</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
<div class="overlay"></div>
<script src="assets/js/main.js"></script>
</body>
</html>

View file

@ -1,382 +0,0 @@
[
{
"extension": "aac",
"description": "AAC audio",
"category": "Music"
},
{
"extension": "abw",
"description": "AbiWord document",
"category": "Document"
},
{
"extension": "arc",
"description": "Archive document (multiple files embedded)",
"category": "Document"
},
{
"extension": "avif",
"description": "AVIF image",
"category": "Image"
},
{
"extension": "avi",
"description": "AVI: Audio Video Interleave",
"category": "Movie"
},
{
"extension": "azw",
"description": "Amazon Kindle eBook format",
"category": "Document"
},
{
"extension": "bin",
"description": "Any kind of binary data",
"category": "Other"
},
{
"extension": "bmp",
"description": "Windows OS/2 Bitmap Graphics",
"category": "Image"
},
{
"extension": "bz",
"description": "BZip archive",
"category": "Other"
},
{
"extension": "bz2",
"description": "BZip2 archive",
"category": "Other"
},
{
"extension": "cda",
"description": "CD audio",
"category": "Music"
},
{
"extension": "csh",
"description": "C-Shell script",
"category": "Executable"
},
{
"extension": "css",
"description": "Cascading Style Sheets (CSS)",
"category": "Other"
},
{
"extension": "csv",
"description": "Comma-separated values (CSV)",
"category": "Document"
},
{
"extension": "doc",
"description": "Microsoft Word",
"category": "Document"
},
{
"extension": "docx",
"description": "Microsoft Word (OpenXML)",
"category": "Document"
},
{
"extension": "eot",
"description": "MS Embedded OpenType fonts",
"category": "Other"
},
{
"extension": "epub",
"description": "Electronic publication (EPUB)",
"category": "Document"
},
{
"extension": "gz",
"description": "GZip Compressed Archive",
"category": "Other"
},
{
"extension": "gif",
"description": "Graphics Interchange Format (GIF)",
"category": "Image"
},
{
"extension": "htm",
"description": "HyperText Markup Language (HTML)",
"category": "Other"
},
{
"extension": "html",
"description": "HyperText Markup Language (HTML)",
"category": "Other"
},
{
"extension": "ico",
"description": "Icon format",
"category": "Image"
},
{
"extension": "ics",
"description": "iCalendar format",
"category": "Document"
},
{
"extension": "jar",
"description": "Java Archive (JAR)",
"category": "Executable"
},
{
"extension": "jpeg",
"description": "JPEG images",
"category": "Image"
},
{
"extension": "jpg",
"description": "JPEG images",
"category": "Image"
},
{
"extension": "js",
"description": "JavaScript",
"category": "Other"
},
{
"extension": "json",
"description": "JSON format",
"category": "Other"
},
{
"extension": "jsonld",
"description": "JSON-LD format",
"category": "Other"
},
{
"extension": "mid",
"description": "Musical Instrument Digital Interface (MIDI)",
"category": "Music"
},
{
"extension": "midi",
"description": "Musical Instrument Digital Interface (MIDI)",
"category": "Music"
},
{
"extension": "mjs",
"description": "JavaScript module",
"category": "Other"
},
{
"extension": "mp3",
"description": "MP3 audio",
"category": "Music"
},
{
"extension": "mp4",
"description": "MP4 video",
"category": "Movie"
},
{
"extension": "mpeg",
"description": "MPEG Video",
"category": "Movie"
},
{
"extension": "mpkg",
"description": "Apple Installer Package",
"category": "Application"
},
{
"extension": "odp",
"description": "OpenDocument presentation document",
"category": "Presentation"
},
{
"extension": "ods",
"description": "OpenDocument spreadsheet document",
"category": "Document"
},
{
"extension": "odt",
"description": "OpenDocument text document",
"category": "Document"
},
{
"extension": "oga",
"description": "OGG audio",
"category": "Music"
},
{
"extension": "ogv",
"description": "OGG video",
"category": "Movie"
},
{
"extension": "ogx",
"description": "OGG",
"category": "Other"
},
{
"extension": "opus",
"description": "Opus audio",
"category": "Music"
},
{
"extension": "otf",
"description": "OpenType font",
"category": "Other"
},
{
"extension": "png",
"description": "Portable Network Graphics",
"category": "Image"
},
{
"extension": "pdf",
"description": "Adobe Portable Document Format (PDF)",
"category": "PDF"
},
{
"extension": "php",
"description": "Hypertext Preprocessor (Personal Home Page)",
"category": "Other"
},
{
"extension": "ppt",
"description": "Microsoft PowerPoint",
"category": "Presentation"
},
{
"extension": "pptx",
"description": "Microsoft PowerPoint (OpenXML)",
"category": "Presentation"
},
{
"extension": "rar",
"description": "RAR archive",
"category": "Other"
},
{
"extension": "rtf",
"description": "Rich Text Format (RTF)",
"category": "Document"
},
{
"extension": "sh",
"description": "Bourne shell script",
"category": "Executable"
},
{
"extension": "svg",
"description": "Scalable Vector Graphics (SVG)",
"category": "Image"
},
{
"extension": "tar",
"description": "Tape Archive (TAR)",
"category": "Other"
},
{
"extension": "tif",
"description": "Tagged Image File Format (TIFF)",
"category": "Image"
},
{
"extension": "tiff",
"description": "Tagged Image File Format (TIFF)",
"category": "Image"
},
{
"extension": "ts",
"description": "MPEG transport stream",
"category": "Movie"
},
{
"extension": "ttf",
"description": "TrueType Font",
"category": "Other"
},
{
"extension": "txt",
"description": "Text, (generally ASCII or ISO 8859-n)",
"category": "Text"
},
{
"extension": "vsd",
"description": "Microsoft Visio",
"category": "Application"
},
{
"extension": "wav",
"description": "Waveform Audio Format",
"category": "Music"
},
{
"extension": "weba",
"description": "WEBM audio",
"category": "Music"
},
{
"extension": "webm",
"description": "WEBM video",
"category": "Movie"
},
{
"extension": "webp",
"description": "WEBP image",
"category": "Image"
},
{
"extension": "woff",
"description": "Web Open Font Format (WOFF)",
"category": "Other"
},
{
"extension": "woff2",
"description": "Web Open Font Format (WOFF)",
"category": "Other"
},
{
"extension": "xhtml",
"description": "XHTML",
"category": "Other"
},
{
"extension": "xls",
"description": "Microsoft Excel",
"category": "Document"
},
{
"extension": "xlsx",
"description": "Microsoft Excel (OpenXML)",
"category": "Document"
},
{
"extension": "xml",
"description": "XML",
"category": "Document"
},
{
"extension": "xul",
"description": "XUL",
"category": "Application"
},
{
"extension": "zip",
"description": "ZIP archive",
"category": "Application"
},
{
"extension": "3gp",
"description": "3GPP audio/video container",
"category": "Movie"
},
{
"extension": "3g2",
"description": "3GPP2 audio/video container",
"category": "Movie"
},
{
"extension": "7z",
"description": "7-zip archive",
"category": "Application"
}
]

24625
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,353 +0,0 @@
{
"name": "botserver",
"version": "5.1.1",
"description": "General Bot Community Edition open-core server.",
"main": "./boot.mjs",
"type": "module",
"bugs": "https://github.com/pragmatismo-io/BotServer/issues",
"homepage": "https://github.com/pragmatismo-io/BotServer/#readme",
"contributors": [
"Rodrigo Rodriguez <me@rodrigorodriguez.com>",
"João Ferreira <joao.parana@gmail.com>",
"Jorge Ramos <jramos@pobox.com>",
"PH <ph.an@outlook.com>",
"Dário Vieira <dario.junior3@gmail.com>",
"Alan Perdomo <alanperdomo@hotmail.com>"
],
"opencv4nodejs": {
"disableAutoBuild": "1"
},
"engines": {
"node": "=22.19.0"
},
"license": "AGPL-3.0",
"preferGlobal": true,
"private": false,
"bin": {
"gbot": "./boot.mjs"
},
"readme": "README.md",
"repository": {
"type": "git",
"url": "https://github.com/GeneralBots/BotServer.git"
},
"scripts": {
"clean": "shx rm -rf node_modules/ dist/ docs/reference",
"tslint": "tslint --fix ./src/*.ts ./packages/**/*.ts -t verbose",
"build": "npm install && npm run build-server && npm run build-gbui",
"build-server": "tsc",
"build-gbui": "cd packages/default.gbui && echo SKIP_PREFLIGHT_CHECK=true >.env && npm install && npm run build",
"build-docs": "typedoc --options typedoc.json src/",
"test": "vitest",
"start": "NODE_NO_WARNINGS=1 node ./boot.mjs --loader ts-node/esm --require ./suppress-node-warnings.cjs",
"debug": "NODE_NO_WARNINGS=1 node ./boot.mjs --loader ts-node/esm --require ./suppress-node-warnings.cjs --inspect",
"watch:build": "tsc --watch"
},
"jest": {
"workerIdleMemoryLimit": "4096MB",
"transform": {
".+\\.tsx?$": "ts-jest"
},
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json"
]
},
"dependencies": {
"@azure/cognitiveservices-computervision": "8.2.0",
"@azure/ms-rest-js": "2.7.0",
"@azure/msal-node": "3.8.0",
"@azure/openai": "2.0.0",
"@azure/search-documents": "12.1.0",
"@azure/storage-blob": "12.28.0",
"@google-cloud/pubsub": "5.2.0",
"@google-cloud/translate": "9.2.0",
"@koa/cors": "5.0.0",
"@koa/router": "14.0.0",
"@langchain/anthropic": "0.3.28",
"@langchain/community": "0.3.56",
"@langchain/core": "0.3.77",
"@langchain/openai": "0.6.13",
"@microsoft/microsoft-graph-client": "3.0.7",
"@push-rpc/core": "1.9.3",
"@push-rpc/http": "1.9.3",
"@push-rpc/openapi": "1.9.3",
"@push-rpc/websocket": "1.9.3",
"@sequelize/core": "7.0.0-alpha.46",
"@sequelize/postgres": "7.0.0-alpha.46",
"@types/validator": "13.15.3",
"adm-zip": "0.5.16",
"alasql": "4.6.6",
"any-shell-escape": "0.1.1",
"arraybuffer-to-buffer": "0.0.7",
"async-mutex": "0.5.0",
"async-promises": "0.2.3",
"async-retry": "1.3.3",
"basic-auth": "2.0.1",
"bcrypt": "6.0.0",
"billboard.js": "3.17.0",
"bluebird": "3.7.2",
"body-parser": "2.2.0",
"botbuilder": "4.23.3",
"botbuilder-ai": "4.23.3",
"botbuilder-dialogs": "4.23.3",
"botframework-connector": "4.23.3",
"botlib-legacy": "5.3.0",
"cd": "0.3.3",
"chalk-animation": "2.0.3",
"chrome-remote-interface": "0.33.3",
"cli-progress": "3.12.0",
"cli-spinner": "0.2.10",
"core-js": "3.45.1",
"cors": "2.8.5",
"csv-database": "0.9.2",
"data-forge": "1.10.4",
"date-diff": "1.0.2",
"docximager": "0.0.4",
"docxtemplater": "3.66.3",
"dotenv-extended": "2.9.0",
"exceljs": "4.4.0",
"express": "^5.1.0",
"express-remove-route": "1.0.0",
"facebook-nodejs-business-sdk": "23.0.2",
"ffmpeg-static": "5.2.0",
"final-stream": "2.0.4",
"formidable": "3.5.4",
"glob": "11.0.3",
"google-libphonenumber": "3.2.43",
"googleapis": "160.0.0",
"hnswlib-node": "3.0.0",
"html-to-md": "0.8.8",
"http-proxy": "1.18.1",
"ibm-watson": "11.0.0",
"icojs": "0.19.5",
"iso-639-1": "3.1.5",
"isomorphic-fetch": "3.0.0",
"jimp": "1.6.0",
"js-md5": "0.8.3",
"json-schema-to-zod": "2.6.1",
"jsqr": "1.4.0",
"just-indent": "0.0.1",
"keyv": "5.5.3",
"koa": "3.0.1",
"koa-body": "6.0.1",
"koa-ratelimit": "6.0.0",
"langchain": "0.3.34",
"language-tags": "2.1.0",
"line-replace": "2.0.1",
"livekit-server-sdk": "2.13.3",
"lodash": "4.17.21",
"luxon": "3.7.2",
"mammoth": "1.11.0",
"mariadb": "3.4.5",
"marked": "16.3.0",
"mime-types": "3.0.1",
"minio": "8.0.6",
"moment": "2.30.1",
"mysql": "2.18.1",
"node-cron": "4.2.1",
"node-gyp": "^11.4.2",
"node-html-parser": "7.0.1",
"node-nlp": "4.27.0",
"nodemailer": "7.0.6",
"nodemon": "3.1.10",
"npm": "11.6.1",
"office-text-extractor": "3.0.3",
"open": "10.2.0",
"open-docxtemplater-image-module": "1.0.3",
"openai": "5.23.1",
"pdf-extraction": "1.0.2",
"pdf-parse": "1.1.1",
"pdf-to-png-converter": "3.10.0",
"pdfjs-dist": "5.4.149",
"pg": "8.16.3",
"phone": "3.1.67",
"pizzip": "3.2.0",
"pptxtemplater": "1.0.5",
"prism-media": "1.3.5",
"public-ip": "8.0.0",
"punycode": "2.3.1",
"puppeteer": "24.22.3",
"puppeteer-extra": "3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2",
"qr-scanner": "1.4.2",
"qrcode": "1.5.4",
"qrcode-reader": "1.0.4",
"qrcode-terminal": "0.12.0",
"readline": "1.3.0",
"reflect-metadata": "0.2.2",
"rimraf": "6.0.1",
"safe-buffer": "5.2.1",
"scanf": "1.2.0",
"sequelize": "6.37.7",
"sequelize-cli": "6.6.3",
"sequelize-typescript": "2.1.6",
"simple-git": "3.28.0",
"speakingurl": "14.0.1",
"strict-password-generator": "1.1.2",
"stripe": "18.5.0",
"super-strong-password-generator": "2.0.2",
"super-strong-password-generator-es": "2.0.2",
"swagger-client": "3.35.7",
"swagger-ui-dist": "5.29.0",
"tabulator-tables": "6.3.1",
"tedious": "18.6.1",
"twilio": "5.10.1",
"twitter-api-v2": "1.27.0",
"typeorm": "0.3.27",
"typescript": "5.9.2",
"url-join": "5.0.0",
"vhost": "3.0.2",
"walk-promise": "0.2.0",
"washyourmouthoutwithsoap": "1.0.2",
"webdav-server": "2.6.2",
"webp-converter": "2.3.3",
"whatsapp-cloud-api": "0.3.1",
"whatsapp-web.js": "1.34.1",
"ws": "8.18.3",
"yaml": "2.8.1",
"yarn": "1.22.22",
"zod-to-json-schema": "3.24.6"
},
"devDependencies": {
"@types/lodash": "4.17.20",
"@types/node": "24.5.2",
"@types/qrcode": "1.5.5",
"@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.44.1",
"tsx": "4.20.6",
"vitest": "3.2.4"
},
"eslintConfig": {
"env": {
"node": true,
"es6": true,
"mocha": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2017
},
"rules": {
"indent": "off",
"linebreak-style": [
"warn",
"unix"
],
"no-unused-vars": [
"warn"
],
"no-undef": [
"warn"
],
"no-console": [
"warn"
],
"no-case-declarations": [
"warn"
],
"no-extra-semi": [
"warn"
],
"no-unreachable": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-useless-escape": [
"warn"
],
"no-constant-condition": [
"warn"
]
}
},
"release": {
"tagFormat": "${version}",
"debug": true,
"branches": [
"main"
],
"verifyConditions": [
"@semantic-release/github"
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog"
],
"prepare": [
"@semantic-release/npm",
{
"path": "@semantic-release/exec",
"cmd": "git status"
},
"@semantic-release/changelog",
{
"path": "@semantic-release/git",
"assets": [
"package.json",
"CHANGELOG.md"
]
}
],
"publish": [
"@semantic-release/npm",
"@semantic-release/github"
],
"analyzeCommits": "simple-commit-message"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
},
"pre-git": {
"commit-msg": "simple",
"pre-commit": [],
"pre-push": [],
"post-commit": [],
"post-checkout": [],
"post-merge": []
}
},
"overrides": {
"lodash.trimend": "npm:lodash@4.17.21",
"lodash.isequal": "npm:lodash@4.17.21",
"node-domexception": "npm:whatwg-url@11.0.0",
"csv-database": {
"fast-csv": "4.3.6"
},
"sequelize-typescript": {
"glob": "~9.0.0"
},
"tough-cookie": "4.1.3",
"phin": "3.7.1",
"xmldom": "npm:@xmldom/xmldom@0.8.10",
"form-data": "2.5.5",
"uuid": "9.0.1",
"har-validator": "5.1.5",
"yaeti": "npm:events@3.3.0",
"text-encoding": "npm:text-encoder-lite@1.0.1",
"docximager": {
"xml2js": "0.5.0"
},
"tar-fs": "3.1.0",
"ws": "8.18.3",
"xml2js": "0.6.2",
"inflight": "npm:lru-cache@10.2.0",
"rimraf": "6.0.1",
"glob": "11.0.3",
"fstream": "npm:fs-extra@11.0.0",
"openai": "5.23.1",
"puppeteer": "24.22.3",
"fluent-ffmpeg": "npm:ffmpeg-static@5.0.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz",
"@nlpjs/xtables": {
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
}
}
}

View file

@ -1,468 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import crypto from 'crypto';
import urlJoin from 'url-join';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { Messages } from '../strings.js';
import { GBAdminService } from '../services/GBAdminService.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBServer } from '../../../src/app.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
class AdminDialog extends IGBDialog {
public static isIntentYes(locale, utterance) {
return utterance.toLowerCase().match(Messages[locale].affirmative_sentences);
}
public static isIntentNo(locale, utterance) {
return utterance.toLowerCase().match(Messages[locale].negative_sentences);
}
public static setup(min: GBMinInstance) {
const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer);
AdminDialog.setupSecurityDialogs(min);
min.dialogs.add(
new WaterfallDialog('/admin-auth', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
const prompt = Messages[locale].authenticate;
return await min.conversationalService.prompt(min, step, prompt);
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (await GBUtil.comparePassword(sensitive, min.instance.adminPass)) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await step.endDialog(true);
} else {
await min.conversationalService.sendText(min, step, Messages[locale].wrong_password);
return await step.replaceDialog('/admin-auth');
}
}
])
);
min.dialogs.add(
new WaterfallDialog('/admin', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
const prompt = Messages[locale].authenticate;
return await min.conversationalService.prompt(min, step, prompt);
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (await GBUtil.comparePassword(sensitive, min.instance.adminPass)) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await min.conversationalService.prompt(min, step, Messages[locale].which_task);
} else {
await min.conversationalService.sendText(min, step, Messages[locale].wrong_password);
return await step.endDialog();
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale: string = step.context.activity.locale;
const text: string = step.context.activity['originalText'];
const cmdName = text.split(' ')[0];
await min.conversationalService.sendText(min, step, Messages[locale].working(cmdName));
let unknownCommand = false;
try {
if (text === 'quit') {
return await step.replaceDialog('/');
} else if (cmdName === 'setupSecurity') {
return await step.beginDialog('/setupSecurity');
} else {
unknownCommand = true;
}
if (unknownCommand) {
await min.conversationalService.sendText(min, step, Messages[locale].unknown_command);
} else {
await min.conversationalService.sendText(min, step, Messages[locale].finished_working);
}
} catch (error) {
await min.conversationalService.sendText(min, step, error.message ? error.message : error);
}
await step.replaceDialog('/ask', { isReturning: true });
}
])
);
min.dialogs.add(
new WaterfallDialog('/install', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
step.activeDialog.state.options.args = (step.options as any).args;
if (step.activeDialog.state.options.confirm) {
return await step.next('sim');
} else {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, Messages[locale].publish_type_yes);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
if (AdminDialog.isIntentYes(locale, step.result)) {
const list = min.core.getParam(min.instance, '.gbapp List', null);
const items = list ? list.split(';') : [];
step.activeDialog.state.options.args;
for (let i = 0; i < items.length; i++) {
for (let j = 0; j < min.appPackages.length; j++) {
if (items[i] === min.appPackages[j]['name']) {
const element = min.appPackages[i];
await element.onExchangeData(min, 'install', null);
break;
}
}
}
} else {
await min.conversationalService.sendText(min, step, Messages[locale].publish_canceled);
}
}
])
);
min.dialogs.add(
new WaterfallDialog('/logs', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const logs = await min.core['getLatestLogs']();
await min.conversationalService.sendText(min, step, logs);
return await step.replaceDialog('/ask', { isReturning: true });
}
])
);
min.dialogs.add(
new WaterfallDialog('/publish', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
step.activeDialog.state.options.confirm = true;
if (step.activeDialog.state.options.confirm || process.env.ADMIN_OPEN_PUBLISH === 'true') {
return await step.next('sim');
} else {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, Messages[locale].publish_type_yes);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
if (AdminDialog.isIntentYes(locale, step.result)) {
let from = step.context.activity.from.id;
let canPublish: Boolean;
if (step.activeDialog.state.options.firstTime) {
canPublish = true;
} else {
canPublish = AdminDialog.canPublish(min, from) || process.env.ADMIN_OPEN_PUBLISH === 'true';
}
if (!canPublish) {
await step.beginDialog('/admin-auth');
} else {
await step.next(true);
}
} else {
await min.conversationalService.sendText(min, step, Messages[locale].publish_canceled);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const locale = step.context.activity.locale;
if (!step.result) {
await min.conversationalService.sendText(min, step, Messages[locale].publish_must_be_admin);
return step.endDialog();
}
const botId = min.instance.botId;
await min.conversationalService.sendText(min, step, Messages[locale].working('Publishing'));
step.activeDialog.state.options.args = (step.options as any).args;
const filename = step.activeDialog.state.options.args
? step.activeDialog.state.options.args.split(' ')[0]
: null;
const packages = [];
let skipError = false;
if (!filename || filename === '') {
await min.conversationalService.sendText(min, step, `Starting publishing for ${botId} packages...`);
packages.push(`${botId}.gbot`);
packages.push(`${botId}.gbtheme`);
packages.push(`${botId}.gbdrive`);
packages.push(`${botId}.gbdata`);
packages.push(`${botId}.gbkb`);
packages.push(`${botId}.gbdialog`);
skipError = true;
} else {
packages.push(filename);
}
await GBUtil.asyncForEach(packages, async packageName => {
let cmd1;
if (
packageName.toLowerCase() === 'gbdialog' ||
packageName.toLowerCase() === 'gbdrive' ||
packageName.toLowerCase() === 'gbdata' ||
packageName.toLowerCase() === 'gbkb' ||
packageName.toLowerCase() === 'gbot' ||
packageName.toLowerCase() === 'gbtheme'
) {
packageName = `${min.botId}.${packageName}`;
}
if (packageName.indexOf('.') !== -1) {
cmd1 = `deployPackage ${process.env.STORAGE_SITE} /${GBConfigService.get('STORAGE_LIBRARY')}/${botId}.gbai/${packageName}`;
} else {
cmd1 = `deployPackage ${packageName}`;
}
if (
(await (deployer as any).getStoragePackageByName(min.instance.instanceId, packageName)) !== null &&
!process.env.DONT_DOWNLOAD
) {
const cmd2 = `undeployPackage ${packageName}`;
await GBAdminService.undeployPackageCommand(cmd2, min);
}
let sec = new SecService();
const member = step.context.activity.from;
const user = await sec.ensureUser(min, member.id, member.name, '', 'web', member.name, null);
await GBAdminService.deployPackageCommand(min, user, cmd1, deployer);
// .gbot updates severals keys in instantece, so min must be updated.
const activeMin = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (activeMin) {
min = activeMin;
}
});
await min.conversationalService.sendText(min, step, `Training is finished.`);
if (!step.activeDialog.state.options.confirm) {
return await step.replaceDialog('/ask', { isReturning: true });
} else {
return await step.endDialog();
}
}
])
);
}
public static canPublish(min: GBMinInstance, phone: string): Boolean {
if (process.env.SECURITY_CAN_PUBLISH !== undefined) {
let list = process.env.SECURITY_CAN_PUBLISH.split(';');
const canPublish = min.core.getParam(min.instance, 'Can Publish', null);
if (canPublish) {
list = list.concat(canPublish.split(';'));
}
let result = list.includes(phone);
if (!result && min.instance.params) {
const params = JSON.parse(min.instance.params);
if (params) {
return list.includes(params['Can Publish']);
}
}
return result;
}
}
private static setupSecurityDialogs(min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/setupSecurity', [
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
const tokenName = (step.activeDialog.state.tokenName = step.options['args']);
if (tokenName) {
((step.activeDialog.state.clientId = min.core.getParam<string>(
min.instance,
`${tokenName} Client ID`,
null
)),
(step.activeDialog.state.host = min.core.getParam<string>(min.instance, `${tokenName} Host`, null)),
(step.activeDialog.state.tenant = min.core.getParam<string>(min.instance, `${tokenName} Tenant`, null)));
}
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.activeDialog.state.tokenName) {
return await step.next(step.options);
}
const locale = step.context.activity.locale;
const prompt = Messages[locale].enter_authenticator_tenant;
return await min.conversationalService.prompt(min, step, prompt);
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
if (step.activeDialog.state.tokenName) {
return await step.next(step.options);
}
step.activeDialog.state.authenticatorTenant = step.context.activity['originalText'];
const locale = step.context.activity.locale;
const prompt = Messages[locale].enter_authenticator_authority_host_url;
return await min.conversationalService.prompt(min, step, prompt);
},
async step => {
min = GBServer.globals.minInstances.find(p => p.botId === min.botId);
step.activeDialog.state.authenticatorAuthorityHostUrl = step.context.activity['originalText'];
const tokenName = step.activeDialog.state.tokenName;
if (!tokenName) {
min.instance.authenticatorAuthorityHostUrl = step.activeDialog.state.authenticatorAuthorityHostUrl;
min.instance.authenticatorTenant = step.activeDialog.state.authenticatorTenant;
await min.adminService.updateSecurityInfo(
min.instance.instanceId,
tokenName ? step.activeDialog.state.tenant : step.activeDialog.state.authenticatorTenant,
tokenName ? step.activeDialog.state.host : step.activeDialog.state.authenticatorAuthorityHostUrl
);
}
const locale = step.context.activity.locale;
const buf = Buffer.alloc(16);
const state = `${min.instance.instanceId}${crypto.randomFillSync(buf).toString('hex')}`;
min.adminService.setValue(min.instance.instanceId, `${tokenName}AntiCSRFAttackState`, state);
const redirectUri = urlJoin(
process.env.BOT_URL,
min.instance.botId,
tokenName ? `/token?value=${tokenName}` : '/token'
);
const scope = tokenName ? '' : 'https://graph.microsoft.com/.default';
const host = tokenName ? step.activeDialog.state.host : 'https://login.microsoftonline.com';
const tenant = tokenName ? step.activeDialog.state.tenant : min.instance.authenticatorTenant;
const clientId = tokenName
? step.activeDialog.state.clientId
: min.instance.marketplaceId
? min.instance.marketplaceId
: GBConfigService.get('MARKETPLACE_ID');
const oauth2 = tokenName ? 'oauth' : 'oauth2';
const url = `${host}/${tenant}/${oauth2}/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}&response_mode=query`;
await min.conversationalService.sendText(min, step, Messages[locale].consent(url));
return await step.replaceDialog('/ask', { isReturning: true });
}
])
);
}
}
export { AdminDialog };

View file

@ -1,73 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib-legacy';
import { Sequelize } from 'sequelize-typescript';
import { AdminDialog } from './dialogs/AdminDialog.js';
import { GuaribasAdmin } from './models/AdminModel.js';
import { GBLogEx } from '../core.gbapp/services/GBLogEx.js';
/**
* The package for admin.gbapp.
*/
export class GBAdminPackage implements IGBPackage {
public sysPackages: IGBPackage[];
public async getDialogs (min: GBMinInstance) {
GBLogEx.verbose(min,`getDialogs called.`);
}
public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
}
public async unloadBot (min: GBMinInstance): Promise<void> {
GBLogEx.verbose(min,`unloadBot called.`);
}
public async onNewSession (min: GBMinInstance, step: GBDialogStep): Promise<void> {
GBLogEx.verbose(min, `onNewSession called.`);
}
public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLogEx.verbose(min,`onExchangeData called.`);
}
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasAdmin]);
}
public async loadBot (min: GBMinInstance): Promise<void> {
AdminDialog.setup(min);
}
}

View file

@ -1,60 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { Column, CreatedAt, DataType, Model, Table, UpdatedAt } from 'sequelize-typescript';
/**
* General settings store.
*/
@Table
export class GuaribasAdmin extends Model<GuaribasAdmin> {
@Column(DataType.INTEGER)
declare instanceId: number;
@Column(DataType.STRING(255))
declare key: string;
@Column(DataType.STRING(4000))
declare value: string;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}

View file

@ -1,341 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import {
GBError,
GBLog,
GBMinInstance,
IGBAdminService,
IGBCoreService,
IGBDeployer,
IGBInstance
} from 'botlib-legacy';
import { FindOptions } from 'sequelize/types';
import urlJoin from 'url-join';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBImporter } from '../../core.gbapp/services/GBImporterService.js';
import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointService.js';
import { GuaribasAdmin } from '../models/AdminModel.js';
import path from 'path';
import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-strong-password-generator';
import crypto from 'crypto';
import fs from 'fs/promises';
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';
let msRestAzure: any = null;
try {
const msRestAzure = require('ms-rest-azure');
} catch (error) {}
let AuthenticationContext: any = null;
try {
const adal = require('adal-node');
AuthenticationContext = adal.AuthenticationContext;
} catch (error) {}
/**
* Services for server administration.
*/
export class GBAdminService implements IGBAdminService {
public static GB_PROMPT: string = 'GeneralBots: ';
public static masterBotInstanceId = 0;
public static StrongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+_-])(?=.{8,})');
public core: IGBCoreService;
constructor(core: IGBCoreService) {
this.core = core;
}
public static generateUuid(): string {
return crypto.randomUUID();
}
public static async getNodeVersion() {
const packageJson = urlJoin(process.cwd(), 'package.json');
const pkg = JSON.parse(await fs.readFile(packageJson, 'utf8'));
return pkg.engines.node.replace('=', '');
}
public static async getADALTokenFromUsername(username: string, password: string) {
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
return (credentials as any).tokenCache._entries[0].accessToken;
}
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;
s.split('').forEach(char => {
if (char == last) counter++;
else {
counter = 0;
last = char;
}
if (counter < r) res += char;
});
return res;
};
return removeRepeatedChars(password, 1);
}
public static getRndReadableIdentifier(): string {
return lowercase_PW(14);
}
public static getNumberIdentifier(digits: number = 14): string {
if (digits <= 0) {
throw new Error('Number of digits should be greater than 0.');
}
const min = 10 ** (digits - 1);
const max = 10 ** digits - 1;
const randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
return randomNumber.toString();
}
/**
* @see https://stackoverflow.com/a/52171480
*/
public static getHash(str: string, seed = 0) {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}
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);
const packagePath = GBUtil.getGBAIPath(min.botId, null, packageName);
const localFolder = path.join('work', packagePath);
await deployer.undeployPackageFromLocalPath(min.instance, localFolder);
}
public static isSharePointPath(path: string) {
return path.indexOf('sharepoint.com') !== -1;
}
public static async deployPackageCommand(
min: GBMinInstance,
user: GuaribasUser,
text: string,
deployer: IGBDeployer
) {
const packageName = text.split(' ')[1];
const folderName = text.substring(text.indexOf(packageName) + packageName.length + 1);
const packageType = path.extname(folderName).substr(1);
const gbaiPath = GBUtil.getGBAIPath(min.instance.botId, packageType, null);
const localFolder = path.join('work', gbaiPath);
await deployer['deployPackage2'](min, user, localFolder, true);
}
public async setValue(instanceId: number, key: string, value: string) {
const options = <FindOptions>{ where: {} };
options.where = { key: key, instanceId: instanceId };
let admin = await GuaribasAdmin.findOne(options);
if (admin === null) {
admin = new GuaribasAdmin();
admin.key = key;
}
admin.value = value;
admin.instanceId = instanceId;
await admin.save();
}
public async updateSecurityInfo(
instanceId: number,
authenticatorTenant: string,
authenticatorAuthorityHostUrl: string
): Promise<IGBInstance> {
const options = <FindOptions>{ where: {} };
options.where = { instanceId: instanceId };
const item = await GuaribasInstance.findOne(options);
item.authenticatorTenant = authenticatorTenant;
item.authenticatorAuthorityHostUrl = authenticatorAuthorityHostUrl;
return item.save();
}
public async getValue(instanceId: number, key: string): Promise<string> {
const options = <FindOptions>{ where: {} };
options.where = { key: key, instanceId: instanceId };
const obj = await GuaribasAdmin.findOne(options);
return obj.value;
}
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;
}
GBLogEx.info(instanceId, `Acquiring token for instanceId: ${instanceId} ${tokenName} (root: ${root}).`);
let expiresOnV;
try {
expiresOnV = await this.getValue(instanceId, `${tokenName}expiresOn`);
} catch (error) {
throw new Error(`/setupSecurity is required before running /publish.`);
}
return new Promise<string>(async (resolve, reject) => {
const instance = await this.core.loadInstanceById(instanceId);
const expiresOn = new Date(expiresOnV);
if (expiresOn.getTime() > new Date().getTime()) {
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 buff = new Buffer(`${clientId}:${clientSecret}`);
const base64 = buff.toString('base64');
const options = {
method: 'POST',
headers: {
Accept: '1.0',
Authorization: `Basic ${base64}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken
})
};
const result = await fetch(url, options);
if (result.status != 200) {
const text = await result.text();
throw new Error(`acquireElevatedToken refreshing token: ${result.status}: ${result.statusText} ${text}.`);
}
const text = await result.text();
const token = JSON.parse(text);
// Saves token to the database.
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}AntiCSRFAttackState`, null);
resolve(token['access_token']);
} else {
const oauth2 = tokenName ? 'oauth' : 'oauth2';
const authorizationUrl = urlJoin(
tokenName ? host : instance.authenticatorAuthorityHostUrl,
tokenName ? tenant : instance.authenticatorTenant,
`/${oauth2}/authorize`
);
const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`);
const resource = tokenName ? '' : 'https://graph.microsoft.com';
const authenticationContext = new AuthenticationContext(authorizationUrl);
authenticationContext.acquireTokenWithRefreshToken(
refreshToken,
tokenName ? clientId : instance.marketplaceId,
tokenName ? clientSecret : instance.marketplacePassword,
resource,
async (err, res) => {
if (err !== null) {
reject(err);
} else {
const token = res;
try {
await this.setValue(instanceId, `${tokenName}accessToken`, token.accessToken);
await this.setValue(instanceId, `${tokenName}refreshToken`, token.refreshToken);
await this.setValue(instanceId, `${tokenName}expiresOn`, token.expiresOn.toString());
resolve(token.accessToken);
} catch (error) {
reject(err);
}
}
}
);
}
}
});
}
public async publish(min: GBMinInstance, packageName: string, republish: boolean): Promise<void> {}
}

View file

@ -1,52 +0,0 @@
export const Messages = {
'en-US': {
authenticate: 'Please, authenticate:',
welcome: 'Welcome to pragmatismo.com.br GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: command => `I'm working on ${command}...`,
finished_working: 'Done.',
unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
hi: text => `Hello, ${text}.`,
undeployPackage: text => `Undeploying package ${text}...`,
deployPackage: text => `Deploying package ${text}...`,
redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `√ Package ${text} undeployed...`,
consent: url => `Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: 'Sorry, wrong password. Please, try again.',
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
enter_authenticator_client_id: `Enter the Client Id GUID: Get from
[this url](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview)`,
enter_authenticator_client_secret: 'Enter the Client Secret:',
publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.',
publish_success: 'Publicação realizada.',
publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.',
publish_canceled: 'Publicação cancelada.'
},
'pt-BR': {
authenticate: 'Please, authenticate:',
welcome: 'Welcome to pragmatismo.com.br GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: command => `I'm working on ${command}...`,
finished_working: 'Done.',
unknown_command: text =>
`Well, but ${text} is not a administrative General Bots command, I will try to search for it.`,
hi: text => `Hello, ${text}.`,
undeployPackage: text => `Undeploying package ${text}...`,
deployPackage: text => `Deploying package ${text}...`,
redeployPackage: text => `Redeploying package ${text}...`,
packageUndeployed: text => `Package ${text} undeployed...`,
consent: url => `Please, consent access to this app at: [Microsoft Online](${url}).`,
wrong_password: 'Sorry, wrong password. Please, try again.',
enter_authenticator_tenant: 'Enter the Authenticator Tenant (eg.: domain.onmicrosoft.com):',
enter_authenticator_authority_host_url: 'Enter the Authority Host URL (eg.: https://login.microsoftonline.com): ',
enter_authenticator_client_id: `Enter the Client Id GUID: Get from
[this url](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview)`,
enter_authenticator_client_secret: 'Enter the Client Secret:',
publish_must_be_admin: 'Seu telefone precisa estar com privilégios administrativos para realizar publicação.',
publish_success: 'Publicação realizada.',
publish_type_yes: 'Por favor, digite *Sim* para continuar com a publicação.',
publish_canceled: 'Publicação cancelada.'
}
};

View file

@ -1,68 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib-legacy';
import { Sequelize } from 'sequelize-typescript';
import { GuaribasConversation, GuaribasConversationMessage } from './models/index.js';
/**
* .gblib Package handler.
*/
export class GBAnalyticsPackage implements IGBPackage {
public sysPackages: IGBPackage[];
public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`);
}
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
GBLog.verbose(`loadPackage called.`);
core.sequelize.addModels([GuaribasConversation, GuaribasConversationMessage]);
}
public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
}
public async loadBot (min: GBMinInstance): Promise<void> {
GBLog.verbose(`loadBot called.`);
}
public async unloadBot (min: GBMinInstance): Promise<void> {
GBLog.verbose(`unloadBot called.`);
}
public async onNewSession (min: GBMinInstance, step: GBDialogStep): Promise<void> {
GBLog.verbose(`onNewSession called.`);
}
public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`);
}
}

View file

@ -1,156 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import {
AutoIncrement,
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
IsUUID,
Length,
Model,
PrimaryKey,
Sequelize,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasChannel, GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
import { GuaribasSubject } from '../../kb.gbapp/models/index.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
/**
* A conversation that groups many messages.
*/
@Table
export class GuaribasConversation extends Model<GuaribasConversation> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare conversationId: number;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER)
declare startSubjectId: number;
@BelongsTo(() => GuaribasSubject)
declare startSubject: GuaribasSubject;
@ForeignKey(() => GuaribasChannel)
@Column(DataType.INTEGER)
declare channelId: string;
@Column(DataType.DATE)
declare rateDate: Date;
@Column(DataType.FLOAT)
declare rate: number;
@Column(DataType.STRING(512))
declare feedback: string;
@CreatedAt
@Column(DataType.DATE)
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
@Column(DataType.STRING(255))
declare text: string;
@ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER)
declare startedByUserId: number;
@BelongsTo(() => GuaribasUser)
declare startedBy: GuaribasUser;
}
/**
* A single message in a conversation.
*/
@Table
export class GuaribasConversationMessage extends Model<GuaribasConversationMessage> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare conversationMessageId: number;
@ForeignKey(() => GuaribasSubject)
@Column(DataType.INTEGER)
declare subjectId: number;
@Column(DataType.TEXT)
declare content: string;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
//tslint:disable-next-line:no-use-before-declare
@ForeignKey(() => GuaribasConversation)
@Column(DataType.INTEGER)
declare conversationId: number;
//tslint:disable-next-line:no-use-before-declare
@BelongsTo(() => GuaribasConversation)
declare conversation: GuaribasConversation;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@ForeignKey(() => GuaribasUser)
@Column(DataType.INTEGER)
declare userId: number;
@BelongsTo(() => GuaribasUser)
declare user: GuaribasUser;
}

View file

@ -1,77 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
import { FindOptions } from 'sequelize/types';
import { GBServer } from '../../../src/app.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { GuaribasConversation, GuaribasConversationMessage } from '../models/index.js';
/**
* Base services for Bot Analytics.
*/
export class AnalyticsService {
public async createConversation(user: GuaribasUser): Promise<GuaribasConversation> {
const conversation = new GuaribasConversation();
conversation.startedBy = user;
conversation.startedByUserId = user.userId;
conversation.instanceId = user.instanceId;
return await conversation.save();
}
public async updateConversationSuggestion(
instanceId: number,
conversationId: string,
feedback: string,
locale: string
): Promise<number> {
const minBoot = GBServer.globals.minBoot as any;
return 0;
}
public async createMessage(
instanceId: number,
conversationId: number,
userId: number,
content: string
): Promise<GuaribasConversationMessage> {
const message = GuaribasConversationMessage.build();
message.content = typeof content === 'object' ? JSON.stringify(content) : content;
message.instanceId = instanceId;
message.userId = userId;
message.conversationId = conversationId;
return await message.save();
}
}

View file

@ -1,7 +0,0 @@
*This is a General Bots open core package, more information can be found on the [BotServer](https://github.com/pragmatismo-io/BotServer) repository.*
This alpha version is using a hack in form of converter to
translate BASIC to TS and string replacements to emulate await code.
See http://jsfiddle.net/roderick/dym05hsy for more info on vb2ts, so
http://stevehanov.ca/blog/index.php?id=92 should be used to run it without
translation and enhance classic BASIC experience.

View file

@ -1,124 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib-legacy';
import { GuaribasSchedule } from '../core.gbapp/models/GBModel.js';
import { Sequelize } from 'sequelize-typescript';
import Koa from 'koa';
import cors from '@koa/cors';
import { createKoaHttpMiddleware } from '@push-rpc/http';
import { GBServer } from '../../src/app.js';
import { SocketServer } from '@push-rpc/core';
import * as koaBody from 'koa-body';
import ratelimit from 'koa-ratelimit';
export function createKoaHttpServer(port: number, getRemoteId: (ctx: Koa.Context) => string, opts: {}): SocketServer {
const { onError, onConnection, middleware } = createKoaHttpMiddleware(getRemoteId);
const app = new Koa();
// Apply the rate-limiting middleware
app.use(
ratelimit({
driver: 'memory', // Use 'memory' for in-memory store
duration: 60000, // 1 minute window
errorMessage: 'Slow down your requests',
id: ctx => ctx.ip, // Identify client by IP address
headers: {
remaining: 'X-RateLimit-Remaining',
reset: 'X-RateLimit-Reset',
total: 'X-RateLimit-Limit'
},
max: 100, // Limit each IP to 100 requests per window
disableHeader: false
})
);
app.use(cors({ origin: '*' }));
app.use(koaBody.koaBody({ jsonLimit: '1024mb', textLimit: '1024mb', formLimit: '1024mb', multipart: true }));
app.use(middleware);
const server = app.listen(port);
const SERVER_TIMEOUT = 60 * 60 * 24 * 1000; // Equals to client RPC set.
server.timeout = SERVER_TIMEOUT;
return {
onError,
onConnection,
close(cb) {
server.close(cb);
}
};
}
/**
* Package for core.gbapp.
*/
export class GBBasicPackage implements IGBPackage {
public sysPackages: IGBPackage[];
public CurrentEngineName = 'guaribas-1.0.0';
public async loadPackage(core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasSchedule]);
}
public async getDialogs(min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`);
}
public async unloadPackage(core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
}
public async unloadBot(min: GBMinInstance): Promise<void> {
GBLog.verbose(`unloadBot called.`);
}
public async onNewSession(min: GBMinInstance, step: GBDialogStep): Promise<void> {
GBLog.verbose(`onNewSession called.`);
}
public async onExchangeData(min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`);
}
public async loadBot(min: GBMinInstance): Promise<void> {
const botId = min.botId;
GBServer.globals.debuggers[botId] = {};
GBServer.globals.debuggers[botId].state = 0;
GBServer.globals.debuggers[botId].breaks = [];
GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
GBServer.globals.debuggers[botId].childProcess = null;
GBServer.globals.debuggers[botId].client = null;
GBServer.globals.debuggers[botId].conversationId = null;
GBServer.globals.debuggers[botId].watermarkMap = {};
}
}

View file

@ -1,75 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import {
AutoIncrement,
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
@Table
//tslint:disable-next-line:max-classes-per-file
export class GuaribasSchedule extends Model<GuaribasSchedule> {
@Column(DataType.STRING(255))
name: string;
@Column(DataType.STRING(255))
schedule: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}

View file

@ -1,61 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBSSR }from '../../core.gbapp/services/GBSSR.js';
export class ChartServices {
/**
* Generate chart image screenshot
* @param {object} options billboard.js generation option object
* @param {string} path screenshot image full path with file name
*/
public static async screenshot (args, path) {
const browser = await GBSSR.createBrowser(null);
const page = await browser.newPage();
// load billboard.js assets from CDN.
await page.addStyleTag({ url: 'https://cdn.jsdelivr.net/npm/billboard.js/dist/theme/datalab.min.css' });
await page.addScriptTag({ url: 'https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.pkgd.min.js' });
await page.evaluate(`bb.generate(${JSON.stringify(args)});`);
const content = await page.$('.bb');
await content.screenshot({
path,
omitBackground: true
});
await page.close();
await browser.close();
}
}

View file

@ -1,216 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBLog, GBMinInstance } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import fs from 'fs/promises';
import SwaggerClient from 'swagger-client';
import { spawn } from 'child_process';
import { CodeServices } from '../../llm.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.
*/
export class DebuggerService {
public async setBreakpoint({ botId, line }) {
GBLogEx.info(botId, `Enabled breakpoint for ${botId} on ${line}.`);
GBServer.globals.debuggers[botId].breaks.push(Number.parseInt(line));
}
public async refactor({ botId, code, change }) {
const service = new CodeServices();
return await service.refactor(code, change);
}
public async resume({ botId }) {
if (GBServer.globals.debuggers[botId].state === 2) {
const client = GBServer.globals.debuggers[botId].client;
await client.Debugger.resume();
GBServer.globals.debuggers[botId].state = 1;
GBServer.globals.debuggers[botId].stateInfo = 'Running (Debug)';
return { status: 'OK' };
} else {
const error = 'Invalid call to resume and state not being debug(2).';
return { error: error };
}
}
public async stop({ botId }) {
GBServer.globals.debuggers[botId].state = 0;
GBServer.globals.debuggers[botId].stateInfo = 'Stopped';
const kill = ref => {
spawn('sh', ['-c', `pkill -9 -f ${ref}`]);
};
kill(GBServer.globals.debuggers[botId].childProcess);
return { status: 'OK' };
}
public async step({ botId }) {
if (GBServer.globals.debuggers[botId].state === 2) {
GBServer.globals.debuggers[botId].stateInfo = 'Break';
const client = GBServer.globals.debuggers[botId].client;
await client.Debugger.stepOver();
return { status: 'OK' };
} else {
const error = 'Invalid call to stepOver and state not being debug(2).';
return { error: error };
}
}
public async getContext({ botId }) {
const conversationsMap = GBServer.globals.debuggers[botId].conversationsMap;
const watermarkMap = GBServer.globals.debuggers[botId].watermarkMap;
const conversationId = conversationsMap[botId];
let messages = [];
const client = GBServer.globals.debuggers[botId].client;
if (client) {
const response = await client.apis.Conversations.Conversations_GetActivities({
conversationId: conversationId,
watermark: watermarkMap[botId]
});
watermarkMap[botId] = response.obj.watermark;
let activities = response.obj.activites;
if (activities && activities.length) {
activities = activities.filter(m => m.from.id === botId && m.type === 'message');
if (activities.length) {
activities.forEach(activity => {
messages.push({ text: activity.text });
GBLogEx.info(botId, `Debugger sending text to API: ${activity.text}`);
});
}
}
}
let messagesText = messages.join('\n');
return {
status: 'OK',
state: GBServer.globals.debuggers[botId].state,
messages: messagesText,
scope: GBServer.globals.debuggers[botId].scope,
scopeInfo: GBServer.globals.debuggers[botId].stateInfo
};
}
public async start({ botId, botApiKey, scriptName }) {
const conversationsMap = GBServer.globals.debuggers[botId].conversationsMap;
let error;
if (!GBServer.globals.debuggers[botId]) {
GBServer.globals.debuggers[botId] = {};
}
if (!scriptName) {
scriptName = 'start';
}
if (GBServer.globals.debuggers[botId].state === 1) {
error = `Cannot DEBUG an already running process. ${botId}`;
return { error: error };
} else if (GBServer.globals.debuggers[botId].state === 2) {
GBLogEx.info(botId, `Releasing execution ${botId} in DEBUG mode.`);
await this.resume({ botId });
return { status: 'OK' };
} else {
GBLogEx.info(botId, `Running ${botId} in DEBUG mode.`);
GBServer.globals.debuggers[botId].state = 1;
GBServer.globals.debuggers[botId].stateInfo = 'Running (Debug)';
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
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;
GBServer.globals.debuggers[botId].conversationId = conversationId;
client.apis.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: `/calldbg ${scriptName}`,
type: 'message',
from: {
id: 'word',
name: 'word'
}
}
});
return { status: 'OK' };
}
}
public async sendMessage({ botId, botApiKey, text }) {
const conversationsMap = GBServer.globals.debuggers[botId].conversationsMap;
let error;
if (!GBServer.globals.debuggers[botId]) {
GBServer.globals.debuggers[botId] = {};
}
if (GBServer.globals.debuggers[botId].state != 1) {
error = `Cannot sendMessage to an stopped process. ${botId}`;
return { error: error };
}
let min: GBMinInstance = GBServer.globals.minInstances.filter(p => p.instance.botId === botId)[0];
const client = GBServer.globals.debuggers[botId].client;
const conversationId = GBServer.globals.debuggers[botId].conversationId;
client.apis.Conversations.Conversations_PostActivity({
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: text,
type: 'message',
from: {
id: 'word',
name: 'word'
}
}
});
return { status: 'OK' };
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,182 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import path from 'path';
import { GBLog, GBMinInstance } from 'botlib-legacy';
import { DialogKeywords } from './DialogKeywords.js';
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 { GBUtil } from '../../../src/util.js';
import fs from 'fs/promises';
import { AzureOpenAI } from 'openai';
import { OpenAIClient } from '@langchain/openai';
/**
* Image processing services of conversation to be called by BASIC.
*/
export class ImageProcessingServices {
/**
* Sharpen the image.
*
* @example file = SHARPEN file
*/
public async sharpen({ pid, file: file }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `Image Processing SHARPEN ${file}.`);
const gbfile = DialogKeywords.getFileByHandle(file);
// TODO: sharp.
return;
}
/**
* SET ORIENTATION VERTICAL
*
* file = MERGE file1, file2, file3
*/
public async mergeImage({ pid, files }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
let paths = [];
await GBUtil.asyncForEach(files, async file => {
const gbfile = DialogKeywords.getFileByHandle(file);
paths.push(gbfile.path);
});
const botId = min.instance.botId;
const packagePath = GBUtil.getGBAIPath(min.botId);
// TODO: const img = await joinImages(paths);
const localName = path.join(
'work',
packagePath,
'cache',
`img-mrg${GBAdminService.getRndReadableIdentifier()}.png`
);
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
// img.toFile(localName);
return { localName: localName, url: url, data: null };
}
/**
* Sharpen the image.
*
* @example file = BLUR file
*/
public async blur({ pid, file: file }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `Image Processing SHARPEN ${file}.`);
const gbfile = DialogKeywords.getFileByHandle(file);
return;
}
public async getImageFromPrompt({ pid, prompt }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `DALL-E: ${prompt}.`);
const azureOpenAIKey = await min.core.getParam(min.instance, 'Azure Open AI Key', null, true);
const azureOpenAIEndpoint = await min.core.getParam(min.instance, 'Azure Open AI Endpoint', null, true);
const azureOpenAIVersion = await (min.core as any)['getParam'](min.instance, 'Azure Open AI Version', null, true);
const azureOpenAIImageModel = await (min.core as any)['getParam'](min.instance, 'Azure Open AI Image Model', null, true);
if (azureOpenAIKey) {
// Initialize the Azure OpenAI client
const client = new AzureOpenAI({
endpoint: azureOpenAIEndpoint,
deployment: azureOpenAIImageModel,
apiVersion: azureOpenAIVersion,
apiKey: azureOpenAIKey
});
// Make a request to the image generation endpoint
const response = await client.images.generate({
prompt: prompt,
n: 1, // Don't include for DALL-E 3 (always generates 1 image)
style: 'vivid', // optional ('natural' or 'vivid')
size: '1024x1024',
quality: 'standard', // optional
});
const gbaiName = GBUtil.getGBAIPath(min.botId);
const localName = path.join('work', gbaiName, 'cache', `DALL-E${GBAdminService.getRndReadableIdentifier()}.png`);
const url = response.data[0].url;
const res = await fetch(url);
let buf: any = Buffer.from(await res.arrayBuffer());
await fs.writeFile(localName, buf, { encoding: null });
GBLogEx.info(min, `DALL-E: ${url} - ${response.data[0].revised_prompt}.`);
return { localName, url };
}
}
public async getCaptionForImage({ pid, imageUrl }) {
const { min, user, params } = await DialogKeywords.getProcessInfo(pid);
const azureOpenAIKey = await min.core.getParam(min.instance, 'Azure Open AI Key', null);
const azureOpenAITextModel = 'gpt-4'; // Specify GPT-4 model here
const azureOpenAIEndpoint = await min.core.getParam(min.instance, 'Azure Open AI Endpoint', null);
const azureOpenAIVersion = await (min.core as any)['getParam'](min.instance, 'Azure Open AI Version', null, true);
if (azureOpenAIKey && azureOpenAITextModel && imageUrl) {
const client = new AzureOpenAI({
apiVersion: azureOpenAIVersion,
apiKey: azureOpenAIKey,
baseURL: azureOpenAIEndpoint
});
const prompt = `Provide a descriptive caption for the image at the following URL: ${imageUrl}`;
const response = await client.completions.create({
model: azureOpenAITextModel,
prompt: prompt,
max_tokens: 50
});
const caption = response['data'].choices[0].text.trim();
GBLogEx.info(min, `Generated caption: ${caption}`);
return { caption };
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,172 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBLog, GBMinInstance, GBService } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import { GuaribasSchedule } from '../../core.gbapp/models/GBModel.js';
import cron from 'node-cron';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../src/util.js';
/**
* @fileoverview Schedule Services.
*/
/**
* Basic services for BASIC manipulation.
*/
export class ScheduleServices extends GBService {
public async deleteScheduleIfAny(min: GBMinInstance, name: string) {
let i = 1;
while (i <= 10) {
const task = min['scheduleMap'] ? min['scheduleMap'][name + i] : null;
if (task) {
task.destroy();
}
const id = `${name};${i}`;
delete min['scheduleMap'][id];
const count = await GuaribasSchedule.destroy({
where: {
instanceId: min.instance.instanceId,
name: id
}
});
if (count > 0) {
GBLogEx.info(min, `Removed ${name} SET SCHEDULE and ${count} rows from storage on: ${min.botId}...`);
}
i++;
}
}
/**
* Finds and update user agent information to a next available person.
*/
public async createOrUpdateSchedule(min: GBMinInstance, schedule: string, name: string): Promise<GuaribasSchedule> {
let record = await GuaribasSchedule.findOne({
where: {
instanceId: min.instance.instanceId,
name: name
}
});
if (record === null) {
record = await GuaribasSchedule.create(<GuaribasSchedule>{
instanceId: min.instance.instanceId,
name: name,
schedule: schedule
});
} else {
record.schedule = schedule;
await record.save();
}
this.ScheduleItem(record, min);
return record;
}
/**
* Load all cached schedule from BASIC SET SCHEDULE keyword.
*/
public async scheduleAll() {
let schedules;
try {
schedules = await GuaribasSchedule.findAll();
let i = 0;
let lastName = '';
await GBUtil.asyncForEach(schedules, async item => {
if (item.name === lastName) {
item.name = item.name + ++i;
} else {
i = 0;
}
let min: GBMinInstance = GBServer.globals.minInstances.filter(
p => p.instance.instanceId === item.instanceId
)[0];
if (min) {
this.ScheduleItem(item, min);
}
});
} catch (error) {
throw new Error(`Cannot schedule: ${error.message}.`);
}
return schedules;
}
private ScheduleItem(item: GuaribasSchedule, min: GBMinInstance) {
GBLogEx.info(min, `Scheduling ${item.name} on ${min.botId}...`);
try {
const options = {
scheduled: true,
timezone: 'America/Sao_Paulo'
};
const task = min['scheduleMap'][item.name];
if (task) {
task.stop();
min['scheduleMap'][item.name] = null;
}
min['scheduleMap'][item.name] = cron.schedule(
item.schedule,
function () {
const finalData = async () => {
let script = item.name.split(';')[0];
let min: GBMinInstance = GBServer.globals.minInstances.filter(
p => p.instance.instanceId === item.instanceId
)[0];
GBLogEx.info(min, `Running .gbdialog word ${item.name} on:${item.schedule}...`);
const pid = GBVMService.createProcessInfo(null, min, 'batch', null);
await GBVMService.callVM(script, min, null, pid);
};
(async () => {
await finalData();
})();
},
options
);
} catch (error) {
GBLogEx.error(min, `Running .gbdialog word ${item.name} : ${error}...`);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,504 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import urlJoin from 'url-join';
import fs from 'fs/promises';
import path from 'path';
import url from 'url';
import { GBLog } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import { DialogKeywords } from './DialogKeywords.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { Mutex } from 'async-mutex';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { SystemKeywords } from './SystemKeywords.js';
import { GBUtil } from '../../../src/util.js';
/**
* Web Automation services of conversation to be called by BASIC.
*/
export class WebAutomationServices {
static isSelector(name: any) {
return name.startsWith('.') || name.startsWith('#') || name.startsWith('[');
}
public static cyrb53 ({pid, str, seed = 0}) {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
public async closeHandles({ pid }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
await DialogKeywords.setOption({ pid, name: "filter", value: null });
// Releases previous allocated OPEN semaphores.
let keys = Object.keys(GBServer.globals.webSessions);
for (let i = 0; i < keys.length; i++) {
const session = GBServer.globals.webSessions[keys[i]];
if (session.activePid === pid) {
session.semaphore.release();
GBLogEx.info(min, `Release for PID: ${pid} done.`);
}
}
}
/**
* Returns the page object.
*
* @example OPEN "https://wikipedia.org"
*/
public async openPage({ pid, handle, sessionKind, sessionName, url, username, password }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `Web Automation OPEN ${sessionName ? sessionName : ''} ${url}.`);
// Try to find an existing handle.
let session;
if (handle) {
session = GBServer.globals.webSessions[handle];
}
else if (sessionName) {
let keys = Object.keys(GBServer.globals.webSessions);
for (let i = 0; i < keys.length; i++) {
if (GBServer.globals.webSessions[keys[i]].sessionName === sessionName) {
session = GBServer.globals.webSessions[keys[i]];
handle = keys[i];
break;
}
}
}
let page;
if (session) {
page = session.page;
// Semaphore logic to block multiple entries on the same session.
if (sessionName) {
GBLogEx.info(min, `Acquiring (1) for PID: ${pid}...`);
const release = await session.semaphore.acquire();
GBLogEx.info(min, `Acquire (1) for PID: ${pid} done.`);
try {
session.activePid = pid;
session.release = release;
} catch {
release();
}
}
}
// Creates the page if it is the first time.
let browser;
if (!page) {
browser = await GBSSR.createBrowser(null);
page = (await browser.pages())[0];
if (username || password) {
await page.authenticate({ pid, username: username, password: password });
}
}
// There is no session yet or it is an unamed session.
if ((!session && sessionKind === 'AS') || !sessionName) {
// A new web session is being created.
handle = WebAutomationServices.cyrb53({pid, str:min.botId + url});
session = {};
session.sessionName = sessionName;
session.page = page;
session.browser = browser;
session.semaphore = new Mutex();
session.activePid = pid;
GBServer.globals.webSessions[handle] = session;
// Only uses semaphore logic in named web sessions.
if (sessionName) {
GBLogEx.info(min, `Acquiring (2) for PID: ${pid}...`);
const release = await session.semaphore.acquire();
session.release = release;
GBLogEx.info(min, `Acquire (2) for PID: ${pid} done.`);
}
}
// WITH is only valid in a previously defined session.
if (!session && sessionKind == 'WITH') {
const error = `NULL session for OPEN WITH #${sessionName}.`;
GBLogEx.error(min, error);
}
await page.goto(url);
return handle;
}
public static getPageByHandle(handle) {
return GBServer.globals.webSessions[handle].page;
}
/**
* Find element on page DOM.
*
* @example GET "selector"
*/
public async getBySelector({ handle, selector, pid }) {
const page = WebAutomationServices.getPageByHandle(handle);
const { min, user } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `Web Automation GET element: ${selector}.`);
await page.waitForSelector(selector);
let elements = await page.$$(selector);
if (elements && elements.length > 1) {
return elements;
} else {
const el = elements[0];
el['originalSelector'] = selector;
el['href'] = await page.evaluate(e => e.getAttribute('href'), el);
el['value'] = await page.evaluate(e => e.getAttribute('value'), el);
el['name'] = await page.evaluate(e => e.getAttribute('name'), el);
el['class'] = await page.evaluate(e => e.getAttribute('class'), el);
return el;
}
}
/**
* Find element on page DOM.
*
* @example GET page,"frameSelector,"elementSelector"
*/
public async getByFrame({pid, handle, frame, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation GET element by frame: ${selector}.`);
await page.waitForSelector(frame);
let frameHandle = await page.$(frame);
const f = await frameHandle.contentFrame();
await f.waitForSelector(selector);
const element = await f.$(selector);
element['originalSelector'] = selector;
element['href'] = await f.evaluate(e => e.getAttribute('href'), element);
element['value'] = await f.evaluate(e => e.getAttribute('value'), element);
element['name'] = await f.evaluate(e => e.getAttribute('name'), element);
element['class'] = await f.evaluate(e => e.getAttribute('class'), element);
element['frame'] = f;
return element;
}
/**
* Simulates a mouse hover an web page element.
*/
public async hover({ pid, handle, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation HOVER element: ${selector}.`);
await this.getBySelector({ handle, selector: selector, pid });
await page.hover(selector);
await this.debugStepWeb(pid, page);
}
/**
* Clicks on an element in a web page.
*
* @example CLICK "#idElement"
*/
public async click({ pid, handle, frameOrSelector, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation CLICK element: ${frameOrSelector}.`);
if (selector) {
await page.waitForSelector(frameOrSelector);
let frameHandle = await page.$(frameOrSelector);
const f = await frameHandle.contentFrame();
await f.waitForSelector(selector);
await f.click(selector);
} else {
await page.waitForSelector(frameOrSelector);
await page.click(frameOrSelector);
}
await this.debugStepWeb(pid, page);
}
private async debugStepWeb(pid, page) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
let debugWeb, lastDebugWeb; // TODO: Add this to pid bag.
let refresh = true;
if (lastDebugWeb) {
refresh = new Date().getTime() - lastDebugWeb.getTime() > 5000;
}
if (debugWeb && refresh) {
const mobile = min.core.getParam(min.instance, 'Bot Admin Number', null);
const filename = page;
if (mobile) {
await new DialogKeywords().sendFileTo({ pid: pid, mobile, filename, caption: 'General Bots Debugger' });
}
lastDebugWeb = new Date();
}
}
/**
* Press ENTER in a web page,useful for logins.
*
* @example PRESS ENTER ON page
*/
public async pressKey({pid, handle, char, frame }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation PRESS ${char} ON element: ${frame}.`);
if (char.toLowerCase() === 'enter') {
char = '\n';
}
if (frame) {
await page.waitForSelector(frame);
let frameHandle = await page.$(frame);
const f = await frameHandle.contentFrame();
await f.keyboard.press(char);
} else {
await page.keyboard.press(char);
}
}
public async linkByText({ pid, handle, text, index }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation CLICK LINK TEXT: ${text} ${index}.`);
if (!index) {
index = 1;
}
const els = await page.$x(`//a[contains(.,'${text}')]`);
await els[index - 1].click();
await this.debugStepWeb(pid, page);
}
public async clickButton({ pid, handle, text, index }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation CLICK BUTTON: ${text} ${index}.`);
if (!index) {
index = 1;
}
const els = await page.$x(`//button[contains(.,'${text}')]`);
await els[index - 1].click();
await this.debugStepWeb(pid, page);
}
/**
* Returns the screenshot of page or element
*
* @example file = SCREENSHOT "#selector"
*/
public async screenshot({ pid, handle, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation SCREENSHOT ${selector}.`);
const gbaiName = GBUtil.getGBAIPath(min.botId);
const localName = path.join('work', gbaiName, 'cache', `screen-${GBAdminService.getRndReadableIdentifier()}.jpg`);
await page.screenshot({ path: localName });
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
GBLogEx.info(min, `WebAutomation: Screenshot captured at ${url}.`);
return { data: null, localName: localName, url: url };
}
/**
* Types the text into the text field.
*
* @example SET page,"selector","text"
*/
public async setElementText({ pid, handle, selector, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
text = `${text}`;
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation TYPE on ${selector}: ${text}.`);
const e = await this.getBySelector({ handle, selector, pid });
await e.click({ clickCount: 3 });
await page.keyboard.press('Backspace');
await e.type(text, { delay: 200 });
await this.debugStepWeb(pid, page);
}
/**
* Performs the download to the .gbdrive Download folder.
*
* @example file = DOWNLOAD element, folder
*/
public async download({ pid, handle, selector, folder }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
const element = await this.getBySelector({ handle, selector, pid });
// https://github.com/GeneralBots/BotServer/issues/311
const container = element['_frame'] ? element['_frame'] : element['_page'];
await page.setRequestInterception(true);
await container.click(element.originalSelector);
const xRequest = await new Promise(resolve => {
page.on('request', interceptedRequest => {
interceptedRequest.abort(); //stop intercepting requests
resolve(interceptedRequest);
});
});
const options = {
encoding: null,
method: xRequest['._method'],
uri: xRequest['_url'],
body: xRequest['_postData'],
headers: xRequest['_headers']
};
const cookies = await page.cookies();
options.headers.Cookie = cookies.map(ck => ck.name + '=' + ck.value).join(';');
GBLogEx.info(min, `DOWNLOADING '${options.uri}...'`);
let local;
let filename;
if (options.uri.indexOf('file://') != -1) {
local = url.fileURLToPath(options.uri);
filename = path.basename(local);
} else {
const getBasenameFormUrl = urlStr => {
const url = new URL(urlStr);
return path.basename(url.pathname);
};
filename = getBasenameFormUrl(options.uri);
}
let result: Buffer;
if (local) {
result = await fs.readFile(local);
} else {
const res = await fetch(options.uri, options);
result = Buffer.from(await res.arrayBuffer());
}
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = min.instance.botId;
// Normalizes all slashes.
folder = folder.replace(/\\/gi, '/');
// Determines full path at source and destination.
const packagePath = GBUtil.getGBAIPath(min.botId, `gbdrive`);
const root = packagePath;
const dstPath = urlJoin(root, folder, filename);
// Checks if the destination contains subfolders that
// need to be created.
folder = await new SystemKeywords().createFolder(folder);
// Performs the conversion operation getting a reference
// to the source and calling /content on drive API.
let file;
try {
file = await client.api(`${baseUrl}/drive/root:/${dstPath}:/content`).put(result);
} catch (error) {
if (error.code === 'nameAlreadyExists') {
GBLogEx.info(min, `DOWNLOAD destination file already exists: ${dstPath}.`);
}
throw error;
}
return file;
}
private async recursiveFindInFrames(inputFrame, selector) {
const frames = inputFrame.childFrames();
const results = await Promise.all(
frames.map(async frame => {
const el = await frame.$(selector);
if (el) return el;
if (frame.childFrames().length > 0) {
return await this.recursiveFindInFrames(frame, selector);
}
return null;
})
);
return results.find(Boolean);
}
public async getTextOf({ pid, handle, frameOrSelector, selector }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const page = WebAutomationServices.getPageByHandle(handle);
GBLogEx.info(min, `Web Automation CLICK element: ${frameOrSelector}.`);
if (frameOrSelector) {
const result = await page.$eval(
frameOrSelector,
(ul) => {
let items = "";
for (let i = 0; i < ul.children.length; i++) {
items = `${ul.children[i].textContent}\n`;
}
return items;
}
)
await this.debugStepWeb(pid, page);
return result;
}
}
}

View file

@ -1,315 +0,0 @@
import crypto2 from 'crypto';
import { spawn } from 'child_process';
import CDP from 'chrome-remote-interface';
import {} from 'child_process';
import net from 'net';
import { GBLog } from 'botlib-legacy';
import { GBServer } from '../../../../src/app.js';
import { DebuggerService } from '../DebuggerService.js';
import { GBLogEx } from '../../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from '../../../../src/util.js';
let finalStream: any = null;
try {
finalStream = require('final-stream');
} catch {}
const waitUntil = condition => {
if (condition()) {
return Promise.resolve();
}
return new Promise(resolve => {
const interval = setInterval(() => {
if (!condition()) {
return;
}
clearInterval(interval);
resolve(0);
}, 0);
});
};
const systemVariables = [
'AggregateError',
'Array',
'ArrayBuffer',
'Atomics',
'BigInt',
'BigInt64Array',
'BigUint64Array',
'Boolean',
'DataView',
'Date',
'Error',
'EvalError',
'FinalizationRegistry',
'Float32Array',
'Float64Array',
'Function',
'Headers',
'Infinity',
'Int16Array',
'Int32Array',
'Int8Array',
'Intl',
'JSON',
'Map',
'Math',
'NaN',
'Number',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'Reflect',
'RegExp',
'Request',
'Response',
'Set',
'SharedArrayBuffer',
'String',
'Symbol',
'SyntaxError',
'TypeError',
'URIError',
'Uint16Array',
'Uint32Array',
'Uint8Array',
'Uint8ClampedArray',
'VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL',
'WeakMap',
'WeakRef',
'WeakSet',
'WebAssembly',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__',
'clearImmediate',
'clearInterval',
'clearTimeout',
'console',
'constructor',
'decodeURI',
'decodeURIComponent',
'dss',
'encodeURI',
'encodeURIComponent',
'escape',
'eval',
'fetch',
'global',
'globalThis',
'hasOwnProperty',
'isFinite',
'isNaN',
'isPrototypeOf',
'parseFloat',
'parseInt',
'process',
'propertyIsEnumerable',
'setImmediate',
'setInterval',
'setTimeout',
'toLocaleString',
'toString',
'undefined',
'unescape',
'valueOf'
];
export const createVm2Pool = ({ min, max, ...limits }) => {
limits = Object.assign(
{
cpu: 100,
memory: 2000,
time: 4000
},
limits
);
let limitError = null;
const ref = crypto2.randomBytes(20).toString('hex');
const kill = x => {
spawn('sh', ['-c', `pkill -9 -f ${ref}`]);
};
let stderrCache = '';
const run = async (code: any, scope: any) => {
// Configure environment variables
const env = Object.assign({}, process.env, {
NODE_ENV: 'production',
NODE_OPTIONS: '' // Clear NODE_OPTIONS if needed
});
const childProcess = spawn(
'/usr/bin/cpulimit',
[
'-ql',
limits.cpu,
'--',
'node',
`${limits.debug ? '--inspect=' + limits.debuggerPort : ''}`,
`--experimental-fetch`,
`--max-old-space-size=${limits.memory}`,
limits.script,
ref
],
{ cwd: limits.cwd, shell: true, env: env }
);
childProcess.stdout.on('data', data => {
childProcess['socket'] = childProcess['socket'] || data.toString().trim();
});
childProcess.stderr.on('data', data => {
stderrCache = stderrCache + data.toString();
if (stderrCache.includes('failed: address already in use')) {
limitError = stderrCache;
kill(process);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = stderrCache;
} else if (
stderrCache.includes('FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory')
) {
limitError = 'code execution exceeed allowed memory';
kill(process);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Fail';
} else if (stderrCache.includes('Debugger attached.')) {
GBLogEx.info(min, `General Bots Debugger attached to Node .gbdialog process for ${limits.botId}.`);
}
});
let socket = null;
await waitUntil(() => childProcess['socket']);
GBServer.globals.debuggers[limits.botId].childProcess = ref;
// Only attach if called by debugger/run.
if (limits.debug) {
const debug = async () => {
return new Promise((resolve, reject) => {
CDP(async client => {
const { Debugger, Runtime } = client;
try {
GBServer.globals.debuggers[limits.botId].client = client;
await client.Debugger.paused(async ({ callFrames, reason, hitBreakpoints }) => {
const frame = callFrames[0];
// Build variable list ignoring system variables of script.
const scopeObjectId = frame.scopeChain[2].object.objectId;
const variables = await Runtime.getProperties({ objectId: scopeObjectId });
let variablesText = '';
if (variables && variables.result) {
await GBUtil.asyncForEach(variables.result, async v => {
if (!systemVariables.filter(x => x === v.name)[0]) {
if (v.value.value) {
variablesText = `${variablesText} \n ${v.name}: ${v.value.value}`;
}
}
});
}
GBServer.globals.debuggers[limits.botId].scope = variablesText;
GBLogEx.info(min, `Breakpoint variables: ${variablesText}`); // (zero-based)
// Processes breakpoint hits.
if (hitBreakpoints.length >= 1) {
GBLogEx.info(min, `Break at line ${frame.location.lineNumber + 1}`); // (zero-based)
GBServer.globals.debuggers[limits.botId].state = 2;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Break';
} else {
GBLog.verbose(`Configuring breakpoints if any for ${limits.botId}...`);
// Waits for debugger and setup breakpoints.
await GBUtil.asyncForEach(GBServer.globals.debuggers[limits.botId].breaks, async brk => {
try {
const { breakpointId } = await client.Debugger.setBreakpoint({
location: {
scriptId: frame.location.scriptId,
lineNumber: brk
}
});
GBLogEx.info(min, `BASIC break defined ${breakpointId} for ${limits.botId}`);
} catch (error) {
GBLogEx.info(min, `BASIC error defining ${brk} for ${limits.botId}. ${error}`);
}
});
await client.Debugger.resume();
}
});
await client.Runtime.runIfWaitingForDebugger();
await client.Debugger.enable();
await client.Runtime.enable();
resolve(1);
} catch (error) {
GBLog.error(error);
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
}
}).on('error', err => {
console.error(err);
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
reject(err);
});
});
};
await debug();
}
socket = net.createConnection(childProcess['socket']);
socket.write(JSON.stringify({ code, scope }) + '\n');
const timer = setTimeout(() => {
limitError = 'code execution took too long and was killed';
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = limitError;
}, limits.time);
try {
let data = await finalStream(socket);
data = JSON.parse(data);
if (!data.length) {
return null;
}
if (data.error) {
throw new Error(data.error);
}
return data.result;
} catch (error) {
throw new Error(limitError || error);
} finally {
kill(childProcess);
GBServer.globals.debuggers[limits.botId].state = 0;
GBServer.globals.debuggers[limits.botId].stateInfo = 'Stopped';
clearTimeout(timer);
}
};
return {
run
};
};

View file

@ -1,70 +0,0 @@
import crypto1 from 'crypto';
import net1 from 'net';
let NodeVM, VMScript;
try {
const NodeVM = require('vm2').NodeVM;
const VMScript = require('vm2').VMScript;
} catch {}
const evaluate = async (script, scope) => {
const vm = new NodeVM({
allowAsync: true,
sandbox: {},
console: 'inherit',
wrapper: 'none',
require: {
builtin: ['stream', 'http', 'https', 'url', 'buffer', 'zlib', 'isomorphic-fetch', 'punycode', 'encoding', 'net'],
root: ['./'],
external: true,
context: 'sandbox'
}
});
const s = new VMScript(script, scope);
return await vm.run(script, scope);
};
const socketName = crypto1.randomBytes(20).toString('hex');
const server = net1.createServer(socket => {
const buffer = [];
const sync = async () => {
const request = buffer.join('').toString();
console.log(request);
if (request.includes('\n')) {
try {
const { code, scope } = JSON.parse(request);
const result = await evaluate(code, {
...scope,
module: null
});
console.log(JSON.stringify({ result }));
socket.write(JSON.stringify({ result }) + '\n');
socket.end();
} catch (error) {
console.log(`RUNTIME: ${error.message}, ${error.stack}`);
socket.write(JSON.stringify({ error: error.message }) + '\n');
socket.end();
}
}
};
socket.on('error', err => {
console.log(err);
});
socket.on('data', data => {
buffer.push(data);
sync();
});
});
server.on('listening', () => {
console.log(`/tmp/vm2-${socketName}.sock`);
});
server.listen(`/tmp/vm2-${socketName}.sock`);

View file

@ -1,10 +0,0 @@
export const Messages = {
'en-US': {
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
choices: 'Please, select one:'
},
'pt-BR': {
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
choices: 'Por favor, selecione:'
}
};

View file

@ -1,16 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,36 +0,0 @@
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

@ -1,12 +0,0 @@
{
"version": "1.0.0",
"theme": "default.gbtheme",
"ui": "default.gbui",
"kb": "default.gbkb",
"title": "Default General Bot",
"description": "Default General Bot",
"whoAmIVideo": "TODO.mp4",
"author": "pragmatismo.com.br",
"license": "AGPL",
"engineName": "guaribas-1.0.0"
}

View file

@ -1,3 +0,0 @@
{
"groups": [{}]
}

View file

@ -1,2 +0,0 @@
{
}

View file

@ -1,7 +0,0 @@
{
"enabledAdmin": "true",
"searchScore": ".45",
"nlpScore": ".80",
"state":"active",
"autoPackageSync": "gbdialog, gbot, gbtheme"
}

View file

@ -1 +0,0 @@
*This is a General Bots BASIC ackage, more information can be found on the [BotServer](https://github.com/GeneralBots/BotServer) repository.*

View file

@ -1,77 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { Messages } from '../strings.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService.js';
/**
* Dialog for the bot explains about itself.
*/
export class BroadcastDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/gb-broadcast', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, 'Type the message and the broadcast will start.');
},
async step => {
// DISABLED: await min.conversationalService['broadcast'](min, step.result);
return await step.next();
}
])
);
}
}

View file

@ -1,114 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { Messages } from '../strings.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBServer } from '../../../src/app.js';
import { GBConversationalService } from '../services/GBConversationalService.js';
import { GBUtil } from '../../../src/util.js';
/**
* Dialog for the bot explains about itself.
*/
export class LanguageDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/language', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, Messages[locale].which_language);
},
async step => {
const locale = step.context.activity.locale;
const list = [
{ name: 'english', code: 'en' },
{ name: 'inglês', code: 'en' },
{ name: 'portuguese', code: 'pt' },
{ name: 'português', code: 'pt' },
{ name: 'français', code: 'fr' },
{ name: 'francês', code: 'fr' },
{ name: 'french', code: 'fr' },
{ name: 'português', code: 'pt' },
{ name: 'spanish', code: 'es' },
{ name: 'espanõl', code: 'es' },
{ name: 'espanhol', code: 'es' },
{ name: 'german', code: 'de' },
{ name: 'deutsch', code: 'de' },
{ name: 'alemão', code: 'de' }
];
let translatorLocale = null;
const text = step.context.activity['originalText'];
await GBUtil.asyncForEach(list, async item => {
if (
GBConversationalService.kmpSearch(text.toLowerCase(), item.name.toLowerCase()) != -1 ||
GBConversationalService.kmpSearch(text.toLowerCase(), item.code.toLowerCase()) != -1
) {
translatorLocale = item.code;
}
});
let sec = new SecService();
let user = await sec.getUserFromSystemId(step.context.activity.from.id);
user = await sec.updateUserLocale(user.userId, translatorLocale);
await min.conversationalService.sendText(min, step, Messages[locale].language_chosen);
await step.replaceDialog('/ask', { firstTime: true });
}
])
);
}
}

View file

@ -1,83 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings.js';
/**
* Dialog for the bot explains about itself.
*/
export class SwitchBotDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/bot', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
return await min.conversationalService.prompt(min, step, 'Qual seria o código de ativação?');
},
async step => {
const sec = new SecService();
const from = step.context.activity.from.id;
const botId = step.result;
const instance = await min.core.loadInstanceByBotId(botId);
await sec.updateUserInstance(from, instance.instanceId);
await min.conversationalService.sendText(min, step, `Opa, vamos lá!`);
return await step.next();
}
])
);
}
}

View file

@ -1,109 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import { Messages } from '../strings.js';
import { GBLogEx } from '../services/GBLogEx.js';
import { GBConfigService } from '../services/GBConfigService.js';
/**
* Dialog for Welcoming people.
*/
export class WelcomeDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup (bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
if (
GBServer.globals.entryPointDialog !== null &&
min.instance.botId === GBConfigService.get('BOT_ID')
) {
return step.replaceDialog(GBServer.globals.entryPointDialog);
}
const locale = step.context.activity.locale;
if (
// TODO: https://github.com/GeneralBots/BotServer/issues/9 !user.once &&
step.context.activity.channelId === 'webchat' &&
min.core.getParam<boolean>(min.instance, 'HelloGoodX', true) === 'true'
) {
// user.once = true;
const a = new Date();
const date = a.getHours();
const msg =
date < 12
? Messages[locale].good_morning
: date < 18
? Messages[locale].good_evening
: Messages[locale].good_night;
await min.conversationalService.sendText(min, step, Messages[locale].hi(msg));
await step.replaceDialog('/ask', { firstTime: true });
if (
step.context.activity !== undefined &&
step.context.activity.type === 'message' &&
step.context.activity.text !== ''
) {
GBLogEx.info(min, `/answer being called from WelcomeDialog.`);
await step.replaceDialog('/answer', { query: step.context.activity.text });
}
}
return await step.next();
}
])
);
}
}

View file

@ -1,82 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { GBConversationalService } from '../services/GBConversationalService.js';
import { Messages } from '../strings.js';
/**
* Dialog for the bot explains about itself.
*/
export class WhoAmIDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup(bot: BotAdapter, min: GBMinInstance) {
min.dialogs.add(
new WaterfallDialog('/whoAmI', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, `${min.instance.description}`);
if (min.instance.whoAmIVideo !== undefined) {
await min.conversationalService.sendText(min, step, Messages[locale].show_video);
await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'video',
data: min.instance.whoAmIVideo.trim()
});
}
await step.replaceDialog('/ask', { isReturning: true });
return await step.next();
}
])
);
}
}

View file

@ -1,80 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib-legacy';
import { Sequelize } from 'sequelize-typescript';
import { BroadcastDialog } from './dialogs/BroadcastDialog.js';
import { LanguageDialog } from './dialogs/LanguageDialog.js';
import { SwitchBotDialog } from './dialogs/SwitchBot.js';
import { WelcomeDialog } from './dialogs/WelcomeDialog.js';
import { WhoAmIDialog } from './dialogs/WhoAmIDialog.js';
import { GuaribasApplications, GuaribasChannel, GuaribasInstance, GuaribasLog, GuaribasPackage } from './models/GBModel.js';
/**
* Package for core.gbapp.
*/
export class GBCorePackage implements IGBPackage {
public sysPackages: IGBPackage[];
public CurrentEngineName = 'guaribas-1.0.0';
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasInstance, GuaribasPackage, GuaribasChannel, GuaribasLog, GuaribasApplications]);
}
public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`);
}
public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
}
public async unloadBot (min: GBMinInstance): Promise<void> {
GBLog.verbose(`unloadBot called.`);
}
public async onNewSession (min: GBMinInstance, step: GBDialogStep): Promise<void> {
GBLog.verbose(`onNewSession called.`);
}
public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`);
}
public async loadBot (min: GBMinInstance): Promise<void> {
WelcomeDialog.setup(min.bot, min);
WhoAmIDialog.setup(min.bot, min);
SwitchBotDialog.setup(min.bot, min);
BroadcastDialog.setup(min.bot, min);
LanguageDialog.setup(min.bot, min);
}
}

View file

@ -1,48 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import {
AutoIncrement,
BelongsTo,
Column,
CreatedAt,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { GuaribasInstance } from './GBModel.js';

View file

@ -1,400 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import {
AutoIncrement,
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt
} from 'sequelize-typescript';
import { IGBInstance } from 'botlib-legacy';
/**
* Base instance data for a bot.
*/
@Table
export class GuaribasInstance extends Model<GuaribasInstance> implements IGBInstance {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare instanceId: number;
@Column(DataType.STRING(255))
declare botEndpoint: string;
@Column(DataType.STRING(255))
declare whoAmIVideo: string;
@Column(DataType.STRING(255))
declare botId: string;
@Column(DataType.STRING(255))
declare title: string;
@Column({ type: DataType.STRING(16) })
declare activationCode: string;
@Column(DataType.STRING(255))
declare description: string;
@Column({ type: DataType.STRING(16) })
declare state: string;
declare version: string;
@Column(DataType.STRING(64))
declare botKey: string;
@Column(DataType.STRING(255))
declare enabledAdmin: boolean;
@Column(DataType.STRING(255))
declare engineName: string;
@Column(DataType.STRING(255))
declare marketplaceId: string;
@Column(DataType.STRING(255))
declare textAnalyticsKey: string;
@Column(DataType.STRING(255))
declare textAnalyticsEndpoint: string;
@Column({ type: DataType.STRING(64) })
declare translatorKey: string;
@Column({ type: DataType.STRING(128) })
declare translatorEndpoint: string;
@Column(DataType.STRING(255))
declare marketplacePassword: string;
@Column(DataType.STRING(255))
declare webchatKey: string;
@Column(DataType.STRING(255))
declare authenticatorTenant: string;
@Column(DataType.STRING(255))
declare authenticatorAuthorityHostUrl: string;
@Column(DataType.STRING(255))
declare cloudSubscriptionId: string;
@Column(DataType.STRING(255))
declare cloudUsername: string;
@Column(DataType.STRING(255))
declare cloudPassword: string;
@Column(DataType.STRING(255))
declare cloudLocation: string;
@Column(DataType.STRING(255))
declare googleBotKey: string;
@Column(DataType.STRING(255))
declare googleChatApiKey: string;
@Column(DataType.STRING(255))
declare googleChatSubscriptionName: string;
@Column(DataType.STRING(255))
declare googleClientEmail: string;
@Column({ type: DataType.STRING(4000) })
declare googlePrivateKey: string;
@Column(DataType.STRING(255))
declare googleProjectId: string;
@Column({ type: DataType.STRING(255) })
declare facebookWorkplaceVerifyToken: string;
@Column({ type: DataType.STRING(255) })
declare facebookWorkplaceAppSecret: string;
@Column({ type: DataType.STRING(512) })
declare facebookWorkplaceAccessToken: string;
@Column(DataType.STRING(255))
declare whatsappBotKey: string;
@Column(DataType.STRING(255))
declare whatsappServiceKey: string;
@Column(DataType.STRING(255))
declare whatsappServiceNumber: string;
@Column(DataType.STRING(255))
declare whatsappServiceUrl: string;
@Column(DataType.STRING(255))
declare smsKey: string;
@Column(DataType.STRING(255))
declare smsSecret: string;
@Column(DataType.STRING(255))
declare smsServiceNumber: string;
@Column(DataType.STRING(255))
declare speechKey: string;
@Column(DataType.STRING(255))
declare speechEndpoint: string;
@Column(DataType.STRING(255))
declare spellcheckerKey: string;
@Column(DataType.STRING(255))
declare spellcheckerEndpoint: string;
@Column(DataType.STRING(255))
declare theme: string;
@Column(DataType.STRING(255))
declare ui: string;
@Column(DataType.STRING(255))
declare kb: string;
@Column(DataType.STRING(255))
declare nlpAppId: string;
@Column(DataType.STRING(255))
declare nlpKey: string;
@Column({ type: DataType.STRING(512) })
declare nlpEndpoint: string;
@Column(DataType.STRING(255))
declare nlpAuthoringKey: string;
@Column(DataType.STRING(255))
declare deploymentPaths: string;
@Column(DataType.STRING(255))
declare searchHost: string;
@Column(DataType.STRING(255))
declare searchKey: string;
@Column(DataType.STRING(255))
declare searchIndex: string;
@Column(DataType.STRING(255))
declare searchIndexer: string;
@Column(DataType.STRING(255))
declare storageUsername: string;
@Column(DataType.STRING(255))
declare storagePassword: string;
@Column(DataType.STRING(255))
declare storageName: string;
@Column(DataType.STRING(255))
declare storageServer: string;
@Column(DataType.STRING(255))
declare storageDialect: string;
@Column(DataType.STRING(255))
declare storagePath: string;
@Column(DataType.STRING(255))
declare adminPass: string;
@Column(DataType.FLOAT)
declare searchScore: number;
@Column(DataType.FLOAT)
declare nlpScore: number;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
@Column(DataType.STRING(4000))
declare params: string;
}
/**
* Each packaged listed for use in a bot instance.
*/
@Table
export class GuaribasPackage extends Model<GuaribasPackage> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare packageId: number;
@Column(DataType.STRING(255))
declare packageName: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@BelongsTo(() => GuaribasInstance)
declare instance: GuaribasInstance;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
@Column({ type: DataType.STRING(512) })
declare custom: string;
}
/**
* A bot channel.
*/
@Table
export class GuaribasChannel extends Model<GuaribasChannel> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare channelId: number;
@Column(DataType.STRING(255))
declare title: string;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}
/**
* An exception that has been thrown.
*/
@Table
//tslint:disable-next-line:max-classes-per-file
export class GuaribasLog extends Model<GuaribasLog> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
declare logId: number;
@Column(DataType.STRING(1024))
declare message: string;
@Column(DataType.STRING(1))
declare kind: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@BelongsTo(() => GuaribasInstance)
declare instance: GuaribasInstance;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}
@Table
//tslint:disable-next-line:max-classes-per-file
export class GuaribasApplications extends Model<GuaribasApplications> {
@Column(DataType.STRING(255))
declare name: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@BelongsTo(() => GuaribasInstance)
declare instance: GuaribasInstance;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}
@Table
//tslint:disable-next-line:max-classes-per-file
export class GuaribasSchedule extends Model<GuaribasSchedule> {
@Column(DataType.STRING(255))
declare name: string;
@Column(DataType.STRING(255))
declare schedule: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
declare instanceId: number;
@BelongsTo(() => GuaribasInstance)
declare instance: GuaribasInstance;
@Column(DataType.DATE)
@CreatedAt
declare createdAt: Date;
@Column(DataType.DATE)
@UpdatedAt
declare updatedAt: Date;
}

View file

@ -1,209 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
'use strict';
import { GBLog } from 'botlib-legacy';
import * as en from 'dotenv-extended';
import path from 'path';
/**
* @fileoverview General Bots server core.
*/
/**
* Base configuration for the server like storage.
*/
export class GBConfigService {
public static getBoolean(value: string): boolean {
return this.get(value) as unknown as boolean;
}
public static getServerPort(): string {
if (process.env.PORT) {
return process.env.PORT;
}
if (process.env.port) {
return process.env.port;
}
return '4242';
}
public static init(): any {
try {
en.load({
encoding: 'utf8',
silent: true,
path: '.env',
defaults: '.env.defaults',
schema: '.env.schema',
errorOnMissing: true,
errorOnExtra: false,
errorOnRegex: true,
includeProcessEnv: false,
assignToProcessEnv: true,
overrideProcessEnv: true
});
} catch (e) {
GBLog.error(e.message);
process.exit(3);
}
}
public static get(key: string) {
let value = GBConfigService.tryGet(key);
if (!value) {
switch (key) {
case 'PORT':
value = this.getServerPort();
break;
case 'GBVM':
value = true;
break;
case 'STORAGE_NAME':
value = null;
break;
case 'WEBDAV_USERNAME':
value = null;
break;
case 'WEBDAV_PASSWORD':
value = null;
break;
case 'CLOUD_USERNAME':
value = undefined;
break;
case 'CLOUD_PASSWORD':
value = undefined;
break;
case 'STORAGE_LIBRARY':
value = path.join(process.env.PWD, 'templates');
break;
case 'BOT_ID':
value = 'default';
break;
case 'CLOUD_SUBSCRIPTIONID':
value = undefined;
break;
case 'CLOUD_LOCATION':
value = undefined;
break;
case 'MARKETPLACE_ID':
value = undefined;
break;
case 'LOG_ON_STORAGE':
value = false;
break;
case 'MARKETPLACE_SECRET':
value = undefined;
break;
case 'STORAGE_DIALECT':
value = 'sqlite';
break;
case 'STORAGE_FILE':
value = './data.db';
break;
case 'GBKB_AUTO_DEPLOY':
value = false;
break;
case 'ADDITIONAL_DEPLOY_PATH':
value = undefined;
break;
case 'STORAGE_SYNC':
value = 'true';
break;
case 'STORAGE_SYNC_ALTER':
value = 'false';
break;
case 'STORAGE_SYNC_FORCE':
value = 'false';
break;
case 'STORAGE_LOGGING':
value = 'false';
break;
case 'STORAGE_ENCRYPT':
value = 'true';
break;
case 'REVERSE_PROXY':
value = undefined;
break;
case 'DISABLE_WEB':
value = 'false';
break;
case 'STORAGE_ACQUIRE_TIMEOUT':
value = 40000;
break;
case 'LOCALE':
value = 'en';
break;
case 'LANGUAGE_DETECTOR':
value = false;
break;
case 'DEFAULT_USER_LANGUAGE':
value = 'en';
break;
case 'DEFAULT_CONTENT_LANGUAGE':
value = 'en';
break;
case 'ENABLE_SPELLING_CHECKER':
value = false;
break;
case 'DEV_GBAI':
value = undefined;
break;
case 'FREE_TIER':
value = true;
break;
case 'BOT_URL':
value = 'http://localhost:4242';
break;
case 'STORAGE_SERVER':
value = undefined;
break;
default:
GBLog.warn(`Invalid key on .env file: '${key}'`);
break;
}
}
return value;
}
public static tryGet(key: string): any {
let value = process.env[`container:${key}`];
if (value === undefined) {
value = process.env[key];
}
return value;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,861 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBLog, GBMinInstance, IGBCoreService, IGBInstallationDeployer, IGBInstance, IGBPackage } from 'botlib-legacy';
import fs from 'fs/promises';
import { Sequelize, SequelizeOptions } from 'sequelize-typescript';
import { Op, Dialect } from 'sequelize';
import { GBServer } from '../../../src/app.js';
import { GBAdminPackage } from '../../admin.gbapp/index.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBAnalyticsPackage } from '../../analytics.gblib/index.js';
import { GBCorePackage } from '../../core.gbapp/index.js';
import { GBCustomerSatisfactionPackage } from '../../customer-satisfaction.gbapp/index.js';
import { GBKBPackage } from '../../kb.gbapp/index.js';
import { GBSecurityPackage } from '../../security.gbapp/index.js';
import { v2 as webdav } from 'webdav-server';
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 { GBSharePointPackage } from '../../sharepoint.gblib/index.js';
import { GBBasicPackage } from '../../basic.gblib/index.js';
import { GBGoogleChatPackage } from '../../google-chat.gblib/index.js';
import path from 'path';
import { GBUtil } from '../../../src/util.js';
import { GBLogEx } from './GBLogEx.js';
import { GBDeployer } from './GBDeployer.js';
import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js';
import csvdb from 'csv-database';
import { SaaSPackage } from '../../saas.gbapp/index.js';
import { Client } from 'minio';
/**
* GBCoreService contains main logic for handling storage services related
* to instance handling. When the server starts a instance is needed and
* if no instance is found a boot instance is created. After that high-level
* instance management methods can be created.
* Core scheduling, base network services are also handled in this service.
*/
export class GBCoreService implements IGBCoreService {
/**
* Data access layer instance.
*/
public sequelize: Sequelize;
/**
* Administrative services.
*/
public adminService: GBAdminService;
/**
* Allows filtering on SQL generated before send to the database.
*/
private queryGenerator: any;
/**
* Custom create table query.
*/
private createTableQuery: (tableName: string, attributes: any, options: any) => string;
/**
* Custom change column query.
*/
private changeColumnQuery: (tableName: string, attributes: any) => string;
/**
* Dialect used. Tested: mssql and sqlite.
*/
private dialect: string;
/**
*
*/
constructor() {
this.adminService = new GBAdminService(this);
}
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {}
/**
* Gets database config and connect to storage. Currently two databases
* are available: SQL Server and SQLite.
*/
public async initStorage(): Promise<any> {
this.dialect = GBConfigService.get('STORAGE_DIALECT');
let port: number | undefined;
let host: string | undefined;
let database: string | undefined;
let username: string | undefined;
let password: string | undefined;
let storage: string | undefined;
if (!['mssql', 'postgres', 'sqlite'].includes(this.dialect)) {
throw new Error(`Unknown or unsupported dialect: ${this.dialect}.`);
}
if (this.dialect === 'mssql' || this.dialect === 'postgres') {
host = GBConfigService.get('STORAGE_SERVER');
database = GBConfigService.get('STORAGE_NAME');
username = GBConfigService.get('STORAGE_USERNAME');
password = GBConfigService.get('STORAGE_PASSWORD');
const portStr = GBConfigService.get('STORAGE_PORT');
port = portStr ? parseInt(portStr, 10) : undefined;
if (!host || !database || !username || !password || !port) {
throw new Error(`Missing required configuration for ${this.dialect}.`);
}
} else if (this.dialect === 'sqlite') {
storage = GBConfigService.get('STORAGE_FILE');
if (!storage) {
throw new Error('STORAGE_FILE is required for SQLite.');
}
if (!(await GBUtil.exists(storage))) {
process.env.STORAGE_SYNC = 'true';
}
}
const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
GBLogEx.info(0, str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
const acquireStr = GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT');
const acquire = acquireStr ? parseInt(acquireStr, 10) : 10000; // Valor padrão de 10 segundos
const sequelizeOptions: SequelizeOptions = {
define: {
freezeTableName: true,
timestamps: false
},
host: host,
port: port,
logging: logging as boolean,
dialect: this.dialect as Dialect,
storage: storage,
quoteIdentifiers: this.dialect === 'postgres',
dialectOptions:
this.dialect === 'mssql'
? {
options: {
trustServerCertificate: true,
encrypt: encrypt
}
}
: {},
pool: {
max: 5,
min: 0,
idle: 10000,
evict: 10000,
acquire: acquire
}
};
this.sequelize = new Sequelize(database, username, password, sequelizeOptions);
}
/**
* Checks wheather storage is acessible or not and opens firewall
* in case of any connection block.
*/
public async checkStorage(installationDeployer: IGBInstallationDeployer) {
try {
await this.sequelize.authenticate();
} catch (error) {
GBLogEx.info(0, 'Opening storage firewall on infrastructure...');
// tslint:disable:no-unsafe-any
if (error.parent.code === 'ELOGIN') {
await this.openStorageFrontier(installationDeployer);
} else {
throw error;
}
// tslint:ensable:no-unsafe-any
}
}
/**
* Syncronizes structure between model and tables in storage.
*/
public async syncDatabaseStructure() {
if (GBConfigService.get('STORAGE_SYNC') === 'true') {
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
GBLogEx.info(0, 'Syncing database...');
return await this.sequelize.sync({
alter: alter,
force: false // Keep it false due to data loss danger.
});
} else {
const msg = `Database synchronization is disabled.`;
GBLogEx.info(0, msg);
}
}
/**
* Loads all items to start several listeners.
*/
public async getLatestLogs(instanceId: number): Promise<string> {
const options = {
where: {
instanceId: instanceId,
state: 'active',
created: {
[Op.gt]: new Date(Date.now() - 60 * 60 * 1000 * 48) // Latest 48 hours.
}
}
};
const list = await GuaribasLog.findAll(options);
let out = 'General Bots Log\n';
await GBUtil.asyncForEach(list, async e => {
out = `${out}\n${e.createdAt} - ${e.message}`;
});
return out;
}
/**
* Loads all items to start several listeners.
*/
public async loadInstances(): Promise<IGBInstance[]> {
if (process.env.LOAD_ONLY) {
const bots = process.env.LOAD_ONLY.split(`;`);
const and = [];
await GBUtil.asyncForEach(bots, async e => {
and.push({ botId: e });
});
const options = {
where: {
[Op.or]: and
},
order: [['instanceId', 'ASC']]
};
return await GuaribasInstance.findAll(options as any);
} else {
const options = {
where: { state: 'active' },
order: [['instanceId', 'ASC']]
};
return await GuaribasInstance.findAll(options as any);
}
}
/**
* Loads just one Bot instance by its internal Id.
*/
public async loadInstanceById(instanceId: number): Promise<IGBInstance> {
const options = { where: { instanceId: instanceId, state: 'active' } };
return await GuaribasInstance.findOne(options);
}
/**
* Loads just one Bot instance.
*/
public async loadInstanceByActivationCode(code: string): Promise<IGBInstance> {
let options = { where: { activationCode: code, state: 'active' } };
return await GuaribasInstance.findOne(options);
}
/**
* Loads just one Bot instance.
*/
public async loadInstanceByBotId(botId: string): Promise<IGBInstance> {
const options = { where: {} };
options.where = { botId: botId, state: 'active' };
return await GuaribasInstance.findOne(options);
}
/**
* Writes .env required to start the full server. Used during
* first startup, when user is asked some questions to create the
* full base environment.
*/
public async writeEnv(instance: IGBInstance) {
const env = `
ADDITIONAL_DEPLOY_PATH=
BOT_ID=${instance.botId}
CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}
CLOUD_LOCATION=${instance.cloudLocation}
CLOUD_USERNAME=${instance.cloudUsername}
CLOUD_PASSWORD=${instance.cloudPassword}
MARKETPLACE_ID=${instance.marketplaceId}
MARKETPLACE_SECRET=${instance.marketplacePassword}
STORAGE_DIALECT=${instance.storageDialect}
STORAGE_SERVER=${instance.storageServer}
STORAGE_NAME=${instance.storageName}
STORAGE_USERNAME=${instance.storageUsername}
STORAGE_PASSWORD=${instance.storagePassword}
STORAGE_SYNC=true
STORAGE_SYNC_ALTER=true
ENDPOINT_UPDATE=true
`;
await fs.writeFile('.env', env);
}
/**
* Setup generic web hooks so .gbapps can expose application logic
* and get called on demand.
*/
public installWebHook(isGet: boolean, url: string, callback: any) {
if (isGet) {
GBServer.globals.server.get(url, (req, res) => {
callback(req, res);
});
} else {
GBServer.globals.server.post(url, (req, res) => {
callback(req, res);
});
}
}
/**
* Defines the entry point dialog to be called whenever a user
* starts talking to the bot.
*/
public setEntryPointDialog(dialogName: string) {
GBServer.globals.entryPointDialog = dialogName;
}
/**
* Replaces the default web application root path used to start the GB
* with a custom home page.
*/
public setWWWRoot(localPath: string) {
GBServer.globals.wwwroot = localPath;
}
/**
* Removes a bot instance from storage.
*/
public async deleteInstance(botId: string) {
const options = { where: {} };
options.where = { botId: botId };
await GuaribasInstance.destroy(options);
}
/**
* Saves a bot instance object to the storage handling
* multi-column JSON based store 'params' field.
*/
public async saveInstance(fullInstance: any) {
const options = { where: {} };
options.where = { botId: fullInstance.botId };
let instance = await GuaribasInstance.findOne(options);
// tslint:disable-next-line:prefer-object-spread
if (instance) {
instance = Object.assign(instance, fullInstance);
} else {
instance = Object.assign(new GuaribasInstance(), fullInstance);
}
try {
instance.params = JSON.stringify(JSON.parse(instance.params));
} catch (error) {
instance.params = JSON.stringify(instance.params);
}
return await instance.save();
}
/**
* Loads all bot instances from object storage, if it's formatted.
*/
public async getApplicationsByInstanceId(appPackages, instanceId: number) {
const options = { where: { instanceId: instanceId } };
const apps = await GuaribasApplications.findAll(options);
let matchingAppPackages = [];
await GBUtil.asyncForEach(appPackages, async appPackage => {
const filenameOnly = path.basename(appPackage.name);
const matchedApp = apps.find(app => app.name === filenameOnly);
if (matchedApp || filenameOnly.endsWith('.gblib')) {
matchingAppPackages.push(appPackage);
}
});
return matchingAppPackages;
}
/**
* Loads all bot instances from object storage, if it's formatted.
*/
public async loadAllInstances(
core: IGBCoreService,
installationDeployer: IGBInstallationDeployer,
proxyAddress: string
) {
GBLogEx.info(0, `Loading instances from storage...`);
let instances: IGBInstance[];
try {
instances = await core.loadInstances();
if (process.env.ENDPOINT_UPDATE === 'true') {
const group = GBConfigService.get('CLOUD_GROUP') ?? GBConfigService.get('BOT_ID');
await GBUtil.asyncForEach(instances, async instance => {
GBLogEx.info(instance.instanceId, `Updating bot endpoint for ${instance.botId}...`);
try {
await installationDeployer.updateBotProxy(
instance.botId,
group,
`${proxyAddress}/api/messages/${instance.botId}`
);
} catch (error) {
if (error.code === 'ResourceNotFound') {
GBLog.warn(`Bot ${instance.botId} not found on resource group ${GBConfigService.get('BOT_ID')}.`);
} else {
throw new Error(`Error updating bot proxy, details: ${error}.`);
}
}
});
}
} catch (error) {
if (error.parent === undefined) {
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
} else {
// Check if storage is empty and needs formatting.
const isInvalidObject = error.parent.number === 208 || error.parent.errno === 1; // MSSQL or SQLITE.
if (isInvalidObject) {
if (GBConfigService.get('STORAGE_SYNC') !== 'true') {
throw new Error(
`Operating storage is out of sync or there is a storage connection error.
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.`
);
}
} else {
throw new Error(`Cannot connect to operating storage: ${error.message}.`);
}
}
}
return instances;
}
/**
* Loads all system packages from 'packages' folder.
*/
public async loadSysPackages(core: GBCoreService): Promise<IGBPackage[]> {
// NOTE: if there is any code before this line a semicolon
// will be necessary before this line.
// Loads all system packages.
const sysPackages: IGBPackage[] = [];
await GBUtil.asyncForEach(
[
GBAdminPackage,
GBCorePackage,
GBSecurityPackage,
GBKBPackage,
GBCustomerSatisfactionPackage,
GBAnalyticsPackage,
GBWhatsappPackage,
GBSharePointPackage,
GBGoogleChatPackage,
GBBasicPackage,
SaaSPackage
],
async e => {
GBLogEx.info(0, `Loading sys package: ${e.name}...`);
const p = Object.create(e.prototype) as IGBPackage;
sysPackages.push(p);
await p.loadPackage(core, core.sequelize);
}
);
return sysPackages;
}
/**
* Verifies that an complex global password has been specified
* before starting the server.
*/
public ensureAdminIsSecured() {}
public async createBootInstance(
core: GBCoreService,
installationDeployer: IGBInstallationDeployer,
proxyAddress: string
) {}
public async openBrowserInDevelopment() {}
private createTableQueryOverride(tableName, attributes, options): string {
let sql: string = this.createTableQuery.apply(this.queryGenerator, [tableName, attributes, options]);
const re1 = /CREATE\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches !== null) {
const table = matches[1];
const re2 = /PRIMARY\s+KEY\s+\(\[[^\]]*\](?:,\s*\[[^\]]*\])*\)/;
sql = sql.replace(re2, (match: string, ...args: any[]): string => {
return `CONSTRAINT [${table}_pk] ${match}`;
});
const re3 = /FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re4 = /\[([^\]]*)\]/g;
sql = sql.replace(re3, (match: string, ...args: any[]): string => {
const fkcols = args[0];
let fkname = table;
let matches2 = re4.exec(fkcols);
while (matches2 !== null) {
fkname += `_${matches2[1]}`;
matches2 = re4.exec(fkcols);
}
return `CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
});
}
return sql;
}
/**
* SQL:
* let sql = '' +
* 'ALTER TABLE [UserGroup]' +
* ' ADD CONSTRAINT [invalid1] FOREIGN KEY ([userId1], [userId2], [userId3]) REFERENCES [User] ([userId1], [userId2], [userId3]) ON DELETE NO ACTION,' +
* ' CONSTRAINT [invalid2] FOREIGN KEY ([groupId1], [groupId2]) REFERENCES [Group] ([groupId1], [groupId2]) ON DELETE NO ACTION, ' +
* ' CONSTRAINT [invalid3] FOREIGN KEY ([instanceId1]) REFERENCES [Instance] ([instanceId1]) ON DELETE NO ACTION'
*/
private changeColumnQueryOverride(tableName, attributes): string {
let sql: string = this.changeColumnQuery.apply(this.queryGenerator, [tableName, attributes]);
const re1 = /ALTER\s+TABLE\s+\[([^\]]*)\]/;
const matches = re1.exec(sql);
if (matches !== null) {
const table = matches[1];
const re2 = /(ADD\s+)?CONSTRAINT\s+\[([^\]]*)\]\s+FOREIGN\s+KEY\s+\((\[[^\]]*\](?:,\s*\[[^\]]*\])*)\)/g;
const re3 = /\[([^\]]*)\]/g;
sql = sql.replace(re2, (match: string, ...args: any[]): string => {
const fkcols = args[2];
let fkname = table;
let matches2 = re3.exec(fkcols);
while (matches2 !== null) {
fkname += `_${matches2[1]}`;
matches2 = re3.exec(fkcols);
}
return `${args[0] ? args[0] : ''}CONSTRAINT [${fkname}_fk] FOREIGN KEY (${fkcols})`;
});
}
return sql;
}
/**
* Opens storage firewall used by the server when starting to get root bot instance.
*/
private async openStorageFrontier(installationDeployer: IGBInstallationDeployer) {
const group = GBConfigService.get('BOT_ID');
const serverName = GBConfigService.get('STORAGE_SERVER').split('.database.windows.net')[0];
await installationDeployer.openStorageFirewall(group, serverName);
}
public async setConfig(min, name: string, value: any): Promise<any> {
if (GBConfigService.get('GB_MODE') === 'legacy') {
// Handles calls for BASIC persistence on sheet files.
GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const maxLines = 512;
const file = 'Config.xlsx';
const packagePath = GBUtil.getGBAIPath(min.botId, `gbot`);
let document = await new SystemKeywords().internalGetDocument(client, baseUrl, packagePath, file);
// Creates book session that will be discarded.
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
// Get the current rows in column A
let results = await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`
)
.get();
const rows = results.values;
let address = '';
let lastEmptyRow = -1;
let isEdit = false;
// Loop through column A to find the row where name matches, or find the next empty row
for (let i = 7; i <= rows.length; i++) {
let result = rows[i - 1][0];
if (result && result.toLowerCase() === name.toLowerCase()) {
address = `B${i}:B${i}`; // Match found, update value in column B
isEdit = true; // We are in editing mode
break;
} else if (!result && lastEmptyRow === -1) {
lastEmptyRow = i; // Store the first empty row if no match is found
}
}
// If no match was found and there's an empty row, add a new entry
if (!isEdit && lastEmptyRow !== -1) {
address = `A${lastEmptyRow}:B${lastEmptyRow}`; // Add new entry in columns A and B
}
// Prepare the request body based on whether it's an edit or add operation
let body = { values: isEdit ? [[value]] : [[name, value]] };
// Update or add the new value in the found address
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`
)
.patch(body);
} else if (GBConfigService.get('GB_MODE') === 'local') {
let packagePath = GBUtil.getGBAIPath(min.botId, `gbot`);
const config = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath, 'config.csv');
const db = await csvdb(config, ['name', 'value'], ',');
if (await db.get({ name: name })) {
await db.edit({ name: name }, { name, value });
} else {
await db.add({ name, value });
}
}
}
/**
* Get a dynamic param from instance. Dynamic params are defined in Config.xlsx
* and loaded into the work folder from comida command.
*
* @param name Name of param to get from instance.
* @param defaultValue Value returned when no param is defined in Config.xlsx.
*/
public getParam<T>(instance: IGBInstance, name: string, defaultValue?: T, platform = false): any {
let value = null;
let params;
name = name.trim();
// Gets .gbot Params from specified bot.
if (instance.params) {
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
params = GBUtil.caseInsensitive(params);
value = params ? params[name] : defaultValue;
}
// Gets specified bot instance values.
params = GBUtil.caseInsensitive(instance['dataValues']);
if (params && !value) {
// Retrieves the value from specified bot instance (no params collection).
value = instance['dataValues'][name];
// If still not found, get from boot bot params.
const minBoot = GBServer.globals.minBoot as any;
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 = GBUtil.caseInsensitive(params);
value = params ? params[name] : defaultValue;
}
// If still did not found in boot bot params, try instance fields.
if (!value) {
value = instance['dataValues'][name];
}
if (!value) {
value = instance[name];
}
}
}
if (value === undefined) {
value = null;
}
if (!value && platform) {
value = process.env[name.replace(/ /g, '_').toUpperCase()];
}
if (value && typeof defaultValue === 'boolean') {
return new Boolean(value ? value.toString().toLowerCase() === 'true' : defaultValue).valueOf();
}
if (value && typeof defaultValue === 'string') {
return value ? value : defaultValue;
}
if (value && typeof defaultValue === 'number') {
return new Number(value ? value : defaultValue ? defaultValue : 0).valueOf();
}
if (typeof value === 'string') {
return value.trim();
}
const ret = value ?? defaultValue;
return ret;
}
/**
* Finds a dynamic param from instance. *
*/
public async findParam<T>(instance: IGBInstance, criteria: string) {
let params = null;
const list = [];
if (instance.params) {
params = typeof instance.params === 'object' ? instance.params : JSON.parse(instance.params);
}
Object.keys(params).forEach(e => {
if (e.toLowerCase().indexOf(criteria.toLowerCase()) !== -1) {
list.push(e);
}
});
return list;
}
public async ensureProxy(port: any): Promise<string> {
return '';
} // Azure removed.
public async ensureFolders(instances, deployer: GBDeployer) {
const storageMode = process.env.GB_MODE;
let libraryPath = GBConfigService.get('STORAGE_LIBRARY');
if (storageMode === 'gbcluster') {
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER,
port: parseInt(process.env.DRIVE_PORT),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET
});
await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const bucketStream = await minioClient.listBuckets();
for await (const bucket of bucketStream) {
if (bucket.name.endsWith('.gbai') && bucket.name.startsWith(process.env.DRIVE_ORG_PREFIX)) {
const botId = bucket.name.replace('.gbai', '').replace(process.env.DRIVE_ORG_PREFIX, '');
await this.syncBotStorage(instances, botId, deployer, libraryPath);
}
}
} else {
if (!(await GBUtil.exists(libraryPath))) {
mkdirp.sync(libraryPath);
}
await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const files = await fs.readdir(libraryPath);
await GBUtil.asyncForEach(files, async file => {
if (file.trim().toLowerCase() !== 'default.gbai' && file.charAt(0) !== '_') {
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 (process.env.GB_MODE === 'local') {
if (!instance) {
GBLog.info(`Importing package ${botId}.gbai...`);
// Creates a bot.
let mobile = null,
email = null;
instance = await deployer.deployBlankBot(botId, mobile, email);
instances.push(instance);
const gbaiPath = path.join(libraryPath, `${botId}.gbai`);
if (!(await GBUtil.exists(gbaiPath))) {
fs.mkdir(gbaiPath, { recursive: true });
}
}
}
return instance;
}
public static async createWebDavServer(minInstances: GBMinInstance[]) {
const userManager = new webdav.SimpleUserManager();
const privilegeManager = new webdav.SimplePathPrivilegeManager();
// Create the WebDAV server
const server = new webdav.WebDAVServer({
port: 1900,
httpAuthentication: new webdav.HTTPDigestAuthentication(userManager, 'Default realm'),
privilegeManager: privilegeManager
});
GBServer.globals.webDavServer = server;
minInstances.forEach(min => {
const user = min.core.getParam(min.instance, 'WebDav Username', GBConfigService.get('WEBDAV_USERNAME'));
const pass = min.core.getParam(min.instance, 'WebDav Password', GBConfigService.get('WEBDAV_PASSWORD'));
if (user && pass) {
const objUser = userManager.addUser(user, pass);
const virtualPath = '/' + min.botId;
let path1 = GBUtil.getGBAIPath(min.botId, null);
const gbaiRoot = path.join(GBConfigService.get('STORAGE_LIBRARY'), path1);
server.setFileSystem(virtualPath, new webdav.PhysicalFileSystem(gbaiRoot), successed => {
GBLogEx.info(min.instance.instanceId, `WebDav online for ${min.botId}...`);
});
privilegeManager.setRights(objUser, virtualPath, ['all']);
}
});
server.start(1900);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBMinInstance, IGBCoreService, IGBInstance } from 'botlib-legacy';
import { CreateOptions } from 'sequelize/types';
import fs from 'fs/promises';
import urlJoin from 'url-join';
import { GBServer } from '../../../src/app.js';
import { GuaribasInstance } from '../models/GBModel.js';
import { GBConfigService } from './GBConfigService.js';
import { GBUtil } from '../../../src/util.js';
/**
* Handles the importing of packages.
*/
export class GBImporter {
public core: IGBCoreService;
constructor (core: IGBCoreService) {
this.core = core;
}
public async importIfNotExistsBotPackage (
botId: string,
packageName: string,
localPath: string,
additionalInstance: IGBInstance = null
) {
const file = urlJoin(localPath, 'settings.json');
let settingsJson = {botId: botId};
if (await GBUtil.exists(file)){
settingsJson = JSON.parse(await fs.readFile(file, 'utf8'));
if (botId === undefined) {
botId = settingsJson.botId;
}
}
let instance: IGBInstance;
if (botId === undefined) {
botId = GBConfigService.get('BOT_ID');
instance = await this.core.loadInstanceByBotId(botId);
if (!instance) {
instance = <IGBInstance>{};
instance.state = 'active';
instance.adminPass = await GBUtil.hashPassword( GBConfigService.get('ADMIN_PASS'));
instance.botId = GBConfigService.get('BOT_ID');
instance.cloudSubscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
instance.cloudLocation = GBConfigService.get('CLOUD_LOCATION');
instance.cloudUsername = GBConfigService.get('CLOUD_USERNAME');
instance.cloudPassword = GBConfigService.get('CLOUD_PASSWORD');
instance.marketplaceId = GBConfigService.get('MARKETPLACE_ID');
instance.marketplacePassword = GBConfigService.get('MARKETPLACE_SECRET');
instance.storageDialect = GBConfigService.get('STORAGE_DIALECT');
instance.storageServer = GBConfigService.get('STORAGE_SERVER');
instance.storageName = GBConfigService.get('STORAGE_NAME');
instance.storageUsername = GBConfigService.get('STORAGE_USERNAME');
instance.storagePassword = GBConfigService.get('STORAGE_PASSWORD');
}
} else {
instance = await this.core.loadInstanceByBotId(botId);
}
if (instance != undefined && !instance.botId) {
console.log(`Null BotId after load instance with botId: ${botId}.`);
} else {
instance = additionalInstance;
}
return await this.createOrUpdateInstanceInternal(instance, botId, localPath, settingsJson);
}
public async createBotInstance (botId: string) {
const fullSettingsJson = <GuaribasInstance>{ };
fullSettingsJson['botId'] = botId;
return await GuaribasInstance.create(fullSettingsJson);
}
private async createOrUpdateInstanceInternal (
instance: IGBInstance,
botId: string,
localPath: string,
settingsJson: any
) {
const fullSettingsJson = { ...GBServer.globals.bootInstance, ...settingsJson,
description:"General Bot", title:botId
};
if (botId !== undefined) {
fullSettingsJson.botId = botId;
}
if (instance !== null) {
instance = { ...instance, ...fullSettingsJson };
return await this.core.saveInstance(instance);
} else {
return await GuaribasInstance.create(fullSettingsJson);
}
}
}

View file

@ -1,90 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBLog, IGBInstance } from 'botlib-legacy';
import { GuaribasLog } from '../models/GBModel.js';
import { GBServer } from '../../../src/app.js';
import { GBConfigService } from './GBConfigService.js';
export class GBLogEx {
private static async logWithLevel(
level: 'error' | 'debug' | 'info' | 'verbose',
minOrInstanceId: any,
message: string
) {
const instanceId = this.normalizeInstanceId(minOrInstanceId);
GBLog[level](`${instanceId}: ${message}`);
await this.log(instanceId, level.charAt(0), message);
}
private static normalizeInstanceId(minOrInstanceId: any): string | number {
if (typeof minOrInstanceId === 'object') {
return minOrInstanceId.instance ? minOrInstanceId.instance.botId : minOrInstanceId.botId;
}
return minOrInstanceId === 0 ? 'default' : minOrInstanceId;
}
public static async error(minOrInstanceId: any, message: string) {
await this.logWithLevel('error', minOrInstanceId, message);
}
public static async debug(minOrInstanceId: any, message: string) {
await this.logWithLevel('debug', minOrInstanceId, message);
}
public static async info(minOrInstanceId: any, message: string) {
await this.logWithLevel('info', minOrInstanceId, message);
}
public static async verbose(minOrInstanceId: any, message: string) {
await this.logWithLevel('verbose', minOrInstanceId, message);
}
/**
* Finds and update user agent information to a next available person.
*/
public static async log(instance, kind: string, message: string): Promise<GuaribasLog> {
if (GBConfigService.get('LOG_ON_STORAGE')) {
message = message ? message.substring(0, 1023) : null;
return await GuaribasLog.create(<GuaribasLog>{
instanceId: instance ? instance : 0,
message: message,
kind: kind
});
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,359 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots SSR support based on https://www.npmjs.com/package/ssr-for-bots.
*/
'use strict';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
import path from 'path';
import fs from 'fs/promises';
import { NextFunction, Request, Response } from 'express';
import urljoin from 'url-join';
import { GBMinInstance } from 'botlib-legacy';
import { GBServer } from '../../../src/app.js';
import { GBLogEx } from './GBLogEx.js';
import urlJoin from 'url-join';
import { GBDeployer } from './GBDeployer.js';
import { GBMinService } from './GBMinService.js';
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
import { GBUtil } from '../../../src/util.js';
const puppeteer = require('puppeteer-extra');
const hidden = require('puppeteer-extra-plugin-stealth');
const { executablePath } = require('puppeteer');
export class GBSSR {
// https://hackernoon.com/tips-and-tricks-for-web-scraping-with-puppeteer-ed391a63d952
// Dont download all resources, we just need the HTML
// Also, this is huge performance/response time boost
private static blockedResourceTypes = [
'image',
'media',
'font',
'texttrack',
'object',
'beacon',
'csp_report',
'imageset'
];
// const whitelist = ["document", "script", "xhr", "fetch"];
private static skippedResources = [
'quantserve',
'adzerk',
'doubleclick',
'adition',
'exelator',
'sharethrough',
'cdn.api.twitter',
'google-analytics',
'googletagmanager',
'google',
'fontawesome',
'facebook',
'analytics',
'optimizely',
'clicktale',
'mixpanel',
'zedo',
'clicksor',
'tiqcdn'
];
public static async preparePuppeteer(profilePath) {
let args = [
'--check-for-update-interval=2592000',
'--disable-accelerated-2d-canvas',
'--disable-dev-shm-usage',
'--disable-features=site-per-process',
'--disable-gpu',
'--no-first-run',
'--no-sandbox',
'--no-default-browser-check'
];
if (profilePath) {
args.push(`--user-data-dir=${profilePath}`);
const preferences = urljoin(profilePath, 'Default', 'Preferences');
if (await GBUtil.exists(preferences)) {
const file = await fs.readFile(preferences, 'utf8');
const data = JSON.parse(file);
data['profile']['exit_type'] = 'none';
await fs.writeFile(preferences, JSON.stringify(data));
}
}
return {
args: args,
ignoreHTTPSErrors: true,
headless: process.env.CHROME_HEADLESS === 'true',
defaultViewport: null,
executablePath: process.env.CHROME_PATH ? process.env.CHROME_PATH : executablePath(),
ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection']
};
}
public static async createBrowser(profilePath): Promise<any> {
const opts = await this.preparePuppeteer(profilePath);
puppeteer.use(hidden());
puppeteer.use(require('puppeteer-extra-plugin-minmax')());
const browser = await puppeteer.launch(opts);
return browser;
}
/**
* Return the HTML of bot default.gbui.
*/
public static async getHTML(min: GBMinInstance) {
const url = urljoin(GBServer.globals.publicAddress, min.botId);
const browser = await GBSSR.createBrowser(null);
const stylesheetContents = {};
let html;
try {
const page = await browser.newPage();
await page.minimize();
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
);
await page.setRequestInterception(true);
page.on('request', request => {
const requestUrl = request.url().split('?')[0].split('#')[0];
if (
GBSSR.blockedResourceTypes.indexOf(request.resourceType()) !== -1 ||
GBSSR.skippedResources.some(resource => requestUrl.indexOf(resource) !== -1)
) {
request.abort();
} else {
request.continue();
}
});
page.on('response', async resp => {
const responseUrl = resp.url();
const sameOrigin = new URL(responseUrl).origin === new URL(url).origin;
const isStylesheet = resp.request().resourceType() === 'stylesheet';
if (sameOrigin && isStylesheet) {
stylesheetContents[responseUrl] = await resp.text();
}
});
const response = await page.goto(url, {
timeout: 120000,
waitUntil: 'networkidle0'
});
await GBUtil.sleep(6000);
// Inject <base> on page to relative resources load properly.
await page.evaluate(url => {
const base = document.createElement('base');
base.href = url;
// Add to top of head, before all other resources.
document.head.prepend(base);
}, url);
// Remove scripts and html imports. They've already executed.
await page.evaluate(() => {
const elements = document.querySelectorAll('script, link[rel="import"]');
elements.forEach(e => {
e.remove();
});
});
// Replace stylesheets in the page with their equivalent <style>.
await page.$$eval(
'link[rel="stylesheet"]',
(links, content) => {
links.forEach((link: any) => {
const cssText = content[link.href];
if (cssText) {
const style = document.createElement('style');
style.textContent = cssText;
link.replaceWith(style);
}
});
},
stylesheetContents
);
html = await page.content();
// Close the page we opened here (not the browser).
await page.close();
} catch (e) {
const html = e.toString();
GBLogEx.error(min, `URL: ${url} Failed with message: ${html}`);
} finally {
await browser.close();
}
return html;
}
public static async ssrFilter(req: Request, res: Response, next) {
let applyOptions = {
prerender: [], // Array containing the user-agents that will trigger the ssr service
exclude: [], // Array containing paths and/or extentions that will be excluded from being prerendered by the ssr service
useCache: true, // Variable that determins if we will use page caching or not
cacheRefreshRate: 86400 // Seconds of which the cache will be kept alive, pass 0 or negative value for infinite lifespan
};
// Default user agents
const prerenderArray = [
'bot',
'googlebot',
'Chrome-Lighthouse',
'DuckDuckBot',
'ia_archiver',
'bingbot',
'yandex',
'baiduspider',
'Facebot',
'facebookexternalhit',
'facebookexternalhit/1.1',
'twitterbot',
'rogerbot',
'linkedinbot',
'embedly',
'quora link preview',
'showyoubot',
'outbrain',
'pinterest',
'slackbot',
'vkShare',
'W3C_Validator'
];
// default exclude array
const excludeArray = ['.xml', '.ico', '.txt', '.json'];
const userAgent: string = req.headers['user-agent'] || '';
const prerender = new RegExp([...prerenderArray, ...applyOptions.prerender].join('|').slice(0, -1), 'i').test(
userAgent
);
const exclude = !new RegExp([...excludeArray, ...applyOptions.exclude].join('|').slice(0, -1)).test(
req.originalUrl
);
// Tries to find botId from URL.
const minBoot = GBServer.globals.minBoot;
let onlyChars: any = /\/([A-Za-z0-9\-\_]+)\/*/.exec(req.originalUrl);
onlyChars = onlyChars ? onlyChars[1] : minBoot.botId;
let botId = req.originalUrl && req.originalUrl === '/' ? minBoot.botId : onlyChars;
let min: GBMinInstance =
req.url === '/'
? minBoot
: GBServer.globals.minInstances.filter(p => p.instance.botId.toLowerCase() === botId.toLowerCase())[0];
if (!min) {
min =
req.url === '/'
? minBoot
: GBServer.globals.minInstances.filter(p =>
p.instance.activationCode ? p.instance.activationCode.toLowerCase() === botId.toLowerCase() : null
)[0];
}
if (!min) {
botId = minBoot.botId;
}
let packagePath = GBUtil.getGBAIPath(botId, `gbui`);
// Checks if the bot has an .gbui published or use default.gbui.
if (!(await GBUtil.exists(packagePath))) {
packagePath = path.join(process.env.PWD, 'packages', `default.gbui`, 'build');
}
let parts = req.url.replace(`/${botId}`, '').split('?');
let url = parts[0];
if (min && req.originalUrl && prerender && exclude && (await GBUtil.exists(packagePath))) {
// Reads from static HTML when a bot is crawling.
packagePath = path.join(process.env.PWD, 'work', packagePath, 'index.html');
const html = await fs.readFile(packagePath, 'utf8');
res.status(200).send(html);
return true;
} else {
// Servers default.gbui web application.
packagePath = path.join(
process.env.PWD,
GBDeployer.deployFolder,
GBMinService.uiPackage,
'build',
url === '/' || url === '' ? `index.html` : url
);
if (GBServer.globals.wwwroot && url === '/') {
packagePath = GBServer.globals.wwwroot + '/index.html'; // TODO.
}
if (!min && !url.startsWith('/images') && GBServer.globals.wwwroot) {
packagePath = path.join(GBServer.globals.wwwroot, url);
}
if (!min && !url.startsWith('/static') && GBServer.globals.wwwroot) {
packagePath = path.join(GBServer.globals.wwwroot, url);
}
if (await GBUtil.exists(packagePath)) {
if (min) {
let html = await fs.readFile(packagePath, 'utf8');
html = html.replace(/\{p\}/gi, min.botId);
html = html.replace(/\{botId\}/gi, min.botId);
const theme = `theme-${await (min.core as any)['getParam'](min.instance, 'Theme Color', 'grey')}`;
html = html.replace(/\{themeColor\}/gi, theme);
html = html.replace(/\{theme\}/gi, min.instance.theme ? min.instance.theme : 'default.gbtheme');
html = html.replace(/\{title\}/gi, min.instance.title);
res.send(html).end();
} else {
res.sendFile(packagePath);
}
return true;
} else {
GBLogEx.verbose(min, `HTTP 404: ${req.url}.`);
res.status(404);
res.end();
}
}
}
}

View file

@ -1,479 +0,0 @@
import fs from 'fs/promises';
import formidable from 'formidable';
import path from 'path';
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';
import { GBUtil } from '../../../../src/util.js';
import urlJoin from 'url-join';
import { GBServer } from '../../../../src/app.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);
let userId = req.query?.userSystemId ? req.query?.userSystemId : req.body?.user?.id;
userId = userId ? userId : req.query.userId;
const activity = createConversationUpdateActivity(serviceUrl, conversationId, userId);
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(`/api/messages/${botId}/v3/directline/conversations/`, reqs);
router.post(`/directline/${botId}/conversations/`, reqs);
router.post(`/directline/conversations/`, reqs);
// Reconnect API
const req3 = (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');
};
router.get('/v3/directline/conversations/:conversationId', req3);
router.get(`/directline/${botId}/conversations/:conversationId`, req3);
// Gets activities from store (local history array for now)
const req45 = (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();
}
};
const req34 = (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();
}
};
router.get(`/directline/${botId}/conversations/:conversationId/activities`, req34);
router.get(`/api/messages/${botId}/v3/directline/conversations/:conversationId/activities`, req34);
// Sends message to bot. Assumes message activities
const res2 = (req, res) => {
const incomingActivity = req.body;
// Make copy of activity. Add required fields
const activity = createMessageActivity(incomingActivity, serviceUrl, req.params.conversationId, req.params['pid']);
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();
}
};
// import { createMessageActivity, getConversation } from './yourModule'; // Update this import as needed
const resupload = async (req, res) => {
// Extract botId from the URL using the pathname
const urlParts = req.url.split('/');
const botId = urlParts[2]; // Assuming the URL is structured like /directline/{botId}/conversations/:conversationId/upload
const conversationId = req.params.conversationId; // Extract conversationId from parameters
const uploadDir = path.join(process.cwd(), 'work', `${botId}.gbai`, 'cache'); // Create upload directory path
// Create the uploads directory if it doesn't exist
await fs.mkdir(uploadDir, { recursive: true });
const form = formidable({
uploadDir, // Use the constructed upload directory
keepExtensions: true, // Keep file extensions
});
form.parse(req, async (err, fields, files) => {
if (err) {
console.log(`Error parsing the file: ${GBUtil.toYAML(err)}.`);
return res.status(400).send('Error parsing the file.');
}
const incomingActivity = fields; // Get incoming activity data
const file = files.file[0]; // Access the uploaded file
const fileName = file['newFilename'];
const fileUrl = urlJoin(GBServer.globals.publicAddress, `${botId}.gbai`,'cache', fileName);
// Create the activity message
let userId = req.query?.userSystemId ? req.query?.userSystemId : req.body?.user?.id;
userId = userId ? userId : req.query?.userId;
const activity = createMessageActivity(incomingActivity, serviceUrl, conversationId, req.params['pid']);
activity.from = { id: userId, name: 'webbot' };
activity.attachments = [{
contentType: 'application/octet-stream', // Adjust as necessary
contentUrl: fileUrl,
name: fileName, // Original filename
}];
const conversation = getConversation(conversationId, conversationInitRequired);
if (conversation) {
// Add the uploaded file info to the activity
activity['fileUrl'] = fileUrl; // Set the file URL
conversation.history.push(activity);
try {
const response = await fetch(botUrl, {
method: 'POST',
body: JSON.stringify(activity),
headers: {
'Content-Type': 'application/json'
}
});
res.status(response.status).json({ id: activity.id });
} catch (fetchError) {
console.error('Error fetching bot:', fetchError);
res.status(500).send('Error processing request.');
}
} else {
// Conversation was never initialized
res.status(400).send('Conversation not initialized.');
}
});
};
router.post(`/api/messages/${botId}/v3/directline/conversations/:conversationId/activities`, res2);
router.post(`/directline/${botId}/conversations/:conversationId/activities`, res2);
router.post(`/directline/${botId}/conversations/:conversationId/upload`, resupload);
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');
});
// TODO: Check duplicate. router.post(`/api/messages/${botId}/v3/directline/conversations/:conversationId/activities`, (req, res) => {
// let activity: IActivity;
// activity = req.body;
// 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,
pid
): IMessageActivity => {
const obj = {
...incomingActivity,
channelId: 'api',
serviceUrl,
conversation: { id: conversationId },
id: uuidv4.v4()
};
return obj;
};
const createConversationUpdateActivity = (serviceUrl: string, conversationId: string, userId: any): IConversationUpdateActivity => {
const activity: IConversationUpdateActivity = {
type: 'conversationUpdate',
channelId: 'api',
serviceUrl,
conversation: { id: conversationId },
id: uuidv4.v4(),
membersAdded: [],
membersRemoved: [],
from: { id: userId, name: 'webbot' }
};
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

@ -1,66 +0,0 @@
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,33 +0,0 @@
export const Messages = {
global_quit: /^(\bsair\b|\bsai\b|\bchega\b|\bexit\b|\bquit\b|\bfinish\b|\bend\b|\bausfahrt\b|\bverlassen\b)/i,
'en-US': {
show_video: 'I will show you a video, please wait...',
good_morning: 'good morning',
good_evening: 'good evening',
good_night: 'good night',
hi: msg => `Hello, ${msg}.`,
very_sorry_about_error: `I'm sorry to inform that there was an error which was recorded to be solved.`,
canceled: 'Canceled. If I can be useful, let me know how',
whats_email: "What's your E-mail address?",
which_language: 'Please, type the language name you would like to talk through.',
validation_enter_valid_email: 'Please enter a valid e-mail.',
language_chosen: "Very good, so let's go...",
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
will_answer_projector: "I'll answer on the projector to a better experience..."
},
'pt-BR': {
show_video: 'Vou te mostrar um vídeo. Por favor, aguarde...',
good_morning: 'bom dia',
good_evening: 'boa tarde',
good_night: 'boa noite',
hi: msg => `Oi, ${msg}.`,
very_sorry_about_error: `Lamento, ocorreu um erro que já foi registrado para ser tratado.`,
canceled: 'Cancelado, avise como posso ser útil novamente.',
whats_email: 'Qual seu e-mail?',
which_language: 'Por favor, digite o idioma que você gostaria de usar para conversarmos.',
validation_enter_valid_email: 'Por favor digite um email válido.',
language_chosen: 'Muito bem, então vamos lá...',
affirmative_sentences: /^(\bsim\b|\bs\b|\bpositivo\b|\bafirmativo\b|\bclaro\b|\bevidente\b|\bsem dúvida\b|\bconfirmo\b|\bconfirmar\b|\bconfirmado\b|\buhum\b|\bsi\b|\by\b|\byes\b|\bsure\b)/i,
will_answer_projector: 'Vou te responder na tela para melhor visualização...'
}
};

View file

@ -1,294 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { GBMinService } from '../../core.gbapp/services/GBMinService.js';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { CSService } from '../services/CSService.js';
import { Messages } from '../strings.js';
/**
* Dialog for feedback collecting.
*/
export class FeedbackDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService();
min.dialogs.add(
new WaterfallDialog('/pleaseNoBadWords', [
async step => {
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, Messages[locale].please_no_bad_words);
return await step.next();
}
])
);
min.dialogs.add(
new WaterfallDialog('/t', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
const sec = new SecService();
let from = GBMinService.userMobile(step);
const profile = await min.userProfile.get(step.context, {});
const args = step.activeDialog.state.options;
// Transfer to...
if (args && args.to) {
// An user from Teams willing to transfer to a WhatsApp user.
await sec.ensureUser(min, args.to, 'Name', '', 'whatsapp', 'Name', null);
await sec.assignHumanAgent(min, args.to, profile.userSystemId);
await min.conversationalService.sendText(
min,
step,
Messages[locale].notify_agent_transfer_done(min.instance.botId)
);
} else {
await min.conversationalService.sendText(min, step, Messages[locale].please_wait_transfering);
const agentSystemId = await sec.assignHumanAgent(min, from);
await min.userProfile.set(step.context, profile);
if (agentSystemId.indexOf('@') !== -1) {
// Agent is from Teams or Google Chat.
const agent = await sec.getUserFromSystemId(agentSystemId);
await min.conversationalService['sendOnConversation'](
min,
agent,
Messages[locale].notify_agent(step.context.activity.from.name)
);
} else {
await min.whatsAppDirectLine.sendToDevice(
agentSystemId,
Messages[locale].notify_agent(step.context.activity.from.name)
);
}
}
return await step.next();
}
])
);
min.dialogs.add(
new WaterfallDialog('/qt', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
const sec = new SecService();
const userSystemId = GBMinService.userMobile(step);
let user = await sec.getUserFromSystemId(userSystemId);
if (user.agentMode === 'self') {
const manualUser = await sec.getUserFromAgentSystemId(userSystemId);
await min.whatsAppDirectLine.sendToDeviceEx(
manualUser.userSystemId,
Messages[locale].notify_end_transfer(min.instance.botId),
locale,
step.context.activity.conversation.id
);
if (userSystemId.charAt(2) === ':' || userSystemId.indexOf('@') > -1) {
// Agent is from Teams or Google Chat.
await min.conversationalService.sendText(
min,
step,
Messages[locale].notify_end_transfer(min.instance.botId)
);
} else {
await min.whatsAppDirectLine.sendToDeviceEx(
userSystemId,
Messages[locale].notify_end_transfer(min.instance.botId),
locale,
step.context.activity.conversation.id
);
}
await sec.updateHumanAgent(userSystemId, min.instance.instanceId, null);
await sec.updateHumanAgent(manualUser.userSystemId, min.instance.instanceId, null);
} else if (user.agentMode === 'human') {
const agent = await sec.getUserFromSystemId(user.agentSystemId);
await min.whatsAppDirectLine.sendToDeviceEx(
user.userSystemId,
Messages[locale].notify_end_transfer(min.instance.botId),
locale,
step.context.activity.conversation.id
);
if (user.agentSystemId.indexOf('@') !== -1) {
// Agent is from Teams or Google Chat.
await min.conversationalService.sendText(
min,
step,
Messages[locale].notify_end_transfer(min.instance.botId)
);
} else {
await min.whatsAppDirectLine.sendToDeviceEx(
user.agentSystemId,
Messages[locale].notify_end_transfer(min.instance.botId),
locale,
step.context.activity.conversation.id
);
}
await sec.updateHumanAgent(user.userSystemId, min.instance.instanceId, null);
await sec.updateHumanAgent(agent.userSystemId, min.instance.instanceId, null);
} else {
if (user.userSystemId.charAt(2) === ':' || userSystemId.indexOf('@') > -1) {
// Agent is from Teams or Google Chat.
await min.conversationalService.sendText(min, step, 'Nenhum atendimento em andamento.');
} else {
await min.whatsAppDirectLine.sendToDeviceEx(
user.userSystemId,
'Nenhum atendimento em andamento.',
locale,
step.context.activity.conversation.id
);
}
}
return await step.next();
}
])
);
min.dialogs.add(
new WaterfallDialog('/feedbackNumber', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
return await step.next();
},
async step => {
const locale = step.context.activity.locale;
const rate = step.result.entity;
const user = await min.userProfile.get(step.context, {});
await service.updateConversationRate(user.conversation, rate);
await min.conversationalService.sendText(min, step, Messages[locale].thanks);
return await step.next();
}
])
);
min.dialogs.add(
new WaterfallDialog('/feedback', [
async step => {
if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) {
return await step.beginDialog('/auth');
} else {
return await step.next(step.options);
}
},
async step => {
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, Messages[locale].about_suggestions);
step.activeDialog.state.cbId = (step.options as any).id;
return await min.conversationalService.prompt(min, step, Messages[locale].what_about_service);
},
async step => {
const fixedLocale = 'en-US';
const user = await min.userProfile.get(step.context, {});
let rate = 1;
if (process.env.PRIVACY_STORE_MESSAGES === 'true') {
// Updates values to perform Bot Analytics.
const analytics = new AnalyticsService();
let rate = await analytics.updateConversationSuggestion(
min.instance.instanceId,
user.conversation.conversationId,
step.result,
user.locale
);
}
if (rate > 0.5) {
await min.conversationalService.sendText(min, step, Messages[fixedLocale].glad_you_liked);
} else {
const message = min.core.getParam<string>(
min.instance,
'Feedback Improve Message',
Messages[fixedLocale].we_will_improve
);
await min.conversationalService.sendText(min, step, message);
}
return await step.replaceDialog('/ask', { isReturning: true });
}
])
);
}
}

View file

@ -1,120 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBMinInstance, IGBDialog } from 'botlib-legacy';
import { BotAdapter } from 'botbuilder';
import { WaterfallDialog } from 'botbuilder-dialogs';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { CSService } from '../services/CSService.js';
import { Messages } from '../strings.js';
/**
* Dialog for collecting quality of answer.
*/
export class QualityDialog extends IGBDialog {
/**
* Setup dialogs flows and define services call.
*
* @param bot The bot adapter.
* @param min The minimal bot instance data.
*/
public static setup(bot: BotAdapter, min: GBMinInstance) {
const service = new CSService();
min.dialogs.add(
new WaterfallDialog('/report', [
async step => {
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, await min.whatsAppDirectLine.getLatestCampaignReport());
return await step.replaceDialog('/ask', { isReturning: true });
}
])
);
min.dialogs.add(
new WaterfallDialog('/check', [
async step => {
const locale = step.context.activity.locale;
await min.conversationalService.sendText(min, step, Messages[locale].check_whatsapp_ok);
return await step.replaceDialog('/ask', { isReturning: true });
}
])
);
min.dialogs.add(
new WaterfallDialog('/quality', [
async step => {
const locale = step.context.activity.locale;
const user = await min.userProfile.get(step.context, {});
const score = step.result;
if (score === 0) {
await min.conversationalService.sendText(min, step, Messages[locale].im_sorry_lets_try);
return await step.next();
} else {
await min.conversationalService.sendText(min, step, Messages[locale].great_thanks);
await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'markdown',
data: {
content: Messages[locale].great_thanks
}
});
await service.insertQuestionAlternate(min.instance.instanceId, user.lastQuestion, user.lastQuestionId);
// Updates values to perform Bot Analytics.
if (process.env.PRIVACY_STORE_MESSAGES === 'true') {
const analytics = new AnalyticsService();
analytics.updateConversationSuggestion(
min.instance.instanceId,
user.conversation,
step.result,
user.locale
);
}
// Goes to the ask loop.
return await step.replaceDialog('/ask', { emptyPrompt: true });
}
}
])
);
}
}

View file

@ -1,73 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { GBDialogStep, GBLog, GBMinInstance, IGBCoreService, IGBPackage } from 'botlib-legacy';
import urlJoin from 'url-join';
import { FeedbackDialog } from './dialogs/FeedbackDialog.js';
import { QualityDialog } from './dialogs/QualityDialog.js';
import { GuaribasQuestionAlternate } from './models/index.js';
import { Sequelize } from 'sequelize-typescript';
/**
* Package for customer-satisfaction.gblib.
*/
export class GBCustomerSatisfactionPackage implements IGBPackage {
public sysPackages: IGBPackage[];
public async getDialogs (min: GBMinInstance) {
GBLog.verbose(`getDialogs called.`);
}
public async unloadPackage (core: IGBCoreService): Promise<void> {
GBLog.verbose(`unloadPackage called.`);
}
public async unloadBot (min: GBMinInstance): Promise<void> {
GBLog.verbose(`unloadBot called.`);
}
public async onNewSession (min: GBMinInstance, step: GBDialogStep): Promise<void> {
GBLog.verbose(`onNewSession called.`);
}
public async onExchangeData (min: GBMinInstance, kind: string, data: any) {
GBLog.verbose(`onExchangeData called.`);
}
public async loadPackage (core: IGBCoreService, sequelize: Sequelize): Promise<void> {
core.sequelize.addModels([GuaribasQuestionAlternate]);
}
public async loadBot (min: GBMinInstance): Promise<void> {
FeedbackDialog.setup(min.bot, min);
QualityDialog.setup(min.bot, min);
}
}

View file

@ -1,63 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
/**
* @fileoverview General Bots server core.
*/
'use strict';
import { AutoIncrement, BelongsTo, Column, DataType, ForeignKey, Model, PrimaryKey, Table } from 'sequelize-typescript';
import { GuaribasInstance } from '../../core.gbapp/models/GBModel.js';
/**
* List of saved alternate questions.
*/
@Table
export class GuaribasQuestionAlternate extends Model<GuaribasQuestionAlternate> {
@PrimaryKey
@AutoIncrement
@Column(DataType.INTEGER)
quickAnswerId: number;
@Column(DataType.STRING(255))
questionTyped: string;
@Column(DataType.STRING(255))
questionText: string;
@ForeignKey(() => GuaribasInstance)
@Column(DataType.INTEGER)
instanceId: number;
@BelongsTo(() => GuaribasInstance)
instance: GuaribasInstance;
}

View file

@ -1,81 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
import { FindOptions, NonNullFindOptions } from 'sequelize/types';
import { GuaribasQuestion } from '../../../packages/kb.gbapp/models/index.js';
import { GuaribasConversation } from '../../analytics.gblib/models/index.js';
import { GuaribasQuestionAlternate } from '../models/index.js';
/**
* Customer Satisfaction Service Layer.
*/
export class CSService {
public async getQuestionFromAlternateText (instanceId: number, text: string): Promise<GuaribasQuestion> {
const questionAlternate = await GuaribasQuestionAlternate.findOne({
where: {
instanceId: instanceId,
questionTyped: text
}
});
let question: GuaribasQuestion = null;
if (questionAlternate !== null) {
question = await GuaribasQuestion.findOne({
where: {
instanceId: instanceId,
questionId: questionAlternate.questionTyped
}
});
}
return question;
}
public async insertQuestionAlternate (
instanceId: number,
questionTyped: string,
questionText: string
): Promise<GuaribasQuestionAlternate> {
return await GuaribasQuestionAlternate.create(<GuaribasQuestionAlternate>{
questionTyped: questionTyped,
questionText: questionText
});
}
public async updateConversationRate (
conversation: GuaribasConversation,
rate: number
): Promise<GuaribasConversation> {
conversation.rate = rate;
return conversation.save();
}
}

View file

@ -1,36 +0,0 @@
export const Messages = {
'en-US': {
about_suggestions: 'Suggestions are welcomed and improve my quality...',
what_about_service: 'What about my service?',
glad_you_liked: "I'm glad you liked. I'm here for you.",
we_will_improve: "Let's take note of that, thanks for sharing.",
what_about_me: 'What about the service, please rate between 1 and 5.',
thanks: 'Thanks!',
im_sorry_lets_try: "I'm sorry. Let's try again...",
great_thanks: 'Great, thanks for sharing your thoughts.',
please_no_bad_words: 'Please, no bad words.',
please_wait_transfering: 'Please, wait while I find an agent to answer you.',
notify_agent: name =>
`New call available for *${name}*, you can answer right here when you are finished, type /qt.`,
notify_end_transfer: botName => `All messages will be now routed to user ${botName}.`,
notify_agent_transfer_done: person => `Now talking directly to ${person}.`,
check_whatsapp_ok: 'If you are seeing this message, WhatsApp API is OK.'
},
'pt-BR': {
about_suggestions: 'Sugestões melhoram muito minha qualidade...',
what_about_service: 'O que achou do meu atendimento?',
glad_you_liked: 'Bom saber que você gostou. Conte comigo.',
we_will_improve: 'Vamos registrar sua questão, obrigado pela sinceridade.',
what_about_me: 'O que achou do meu atendimento, de 1 a 5?',
thanks: 'Obrigado!',
im_sorry_lets_try: 'Desculpe-me, vamos tentar novamente.',
great_thanks: 'Ótimo, obrigado por contribuir com sua resposta.',
please_no_bad_words: 'Por favor, sem palavrões!',
please_wait_transfering: 'Por favor, aguarde enquanto eu localizo alguém para te atender.',
notify_agent: name =>
`Existe um novo atendimento para *${name}*, por favor, responda aqui mesmo para a pessoa. Para finalizar, digite /qt.`,
notify_end_transfer: botName => `Falando novamente com o bot ${botName}.`,
notify_agent_transfer_done: person => `Todas as mensagens agora sendo transmitidas para ${person}.`,
check_whatsapp_ok: 'Se você está recebendo esta mensagem, significa que a API do WhatsApp está OK.'
}
};

View file

@ -1 +0,0 @@
https://github.com/microsoft/BotFramework-WebChat/issues/5552 2025.09

View file

@ -1,60 +0,0 @@
{
"name": "default.gbui",
"version": "3.0.0",
"private": false,
"repository": "https://github.com/GeneralBots/BotServer",
"description": "Default web interface for General Bots open-core",
"license": "AGPL-3.0",
"homepage": ".",
"dependencies": {
"@midudev/react-static-content": "1.0.4",
"ajv": "8.17.1",
"botframework-directlinejs": "0.15.5",
"botframework-webchat": "4.18.0",
"deep-extend": "0.6.0",
"eslint": "9.10.0",
"fetch": "1.1.0",
"msal": "^1.4.18",
"powerbi-client": "2.23.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-helmet": "6.1.0",
"react-player": "2.16.0",
"react-scripts": "5.0.1",
"react-super-seo": "1.1.9",
"react-transition-group": "4.4.5",
"rollup-plugin-terser": "^7.0.2",
"rxjs": "7.8.1",
"sourcemap-codec": "^1.4.8",
"url-join": "5.0.0",
"webpack": "5.94.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"overrides": {
"postcss": "8.4.31",
"webpack-dev-server": "5.2.2",
"inflight": "lru-cache",
"stable": "0.1.8",
"rimraf": "5.0.0",
"domexception": "4.0.0",
"q": "2.0.0",
"uuid": "10.0.0",
"workbox-cacheable-response": "7.0.0",
"workbox-background-sync": "7.0.0",
"workbox-google-analytics": "7.0.0",
"svgo": "3.0.0",
"core-js": "3.38.0",
"glob": "10.0.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

View file

@ -1,58 +0,0 @@
<!--
/*****************************************************************************\
| █████ █████ ██ █ █████ █████ ████ ██ ████ █████ █████ ███ ® |
| ██ █ ███ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
| ██ ███ ████ █ ██ █ ████ █████ ██████ ██ ████ █ █ █ ██ |
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
-->
<!DOCTYPE html>
<html style="width: 100%; height: 100%" class="{themeColor}">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/themes/{theme}/css/colors.css" />
<link rel="stylesheet" type="text/css" href="/themes/{theme}/css/default.css" />
<title>{title} | General Bots</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
const input = document.querySelector('.webchat__send-box-text-box__input');
if (input) {
input.focus();
}
}, 3000); // Adjust timing as needed
});
</script>
</head>
<body style="background-color: black">
<div id="root"></div>
</body>
</html>

View file

@ -1,386 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
import React from 'react';
import GBMarkdownPlayer from './players/GBMarkdownPlayer.js';
import GBImagePlayer from './players/GBImagePlayer.js';
import GBVideoPlayer from './players/GBVideoPlayer.js';
import GBUrlPlayer from './players/GBUrlPlayer.js';
import GBMultiUrlPlayer from './players/GBMultiUrlPlayer.js';
import GBLoginPlayer from './players/GBLoginPlayer.js';
import GBBulletPlayer from './players/GBBulletPlayer.js';
import SidebarMenu from './components/SidebarMenu.js';
import SEO from './components/SEO.js';
import GBCss from './components/GBCss.js';
import { DirectLine } from 'botframework-directlinejs';
import { ConnectionStatus } from 'botframework-directlinejs';
import ReactWebChat from 'botframework-webchat';
import { UserAgentApplication } from 'msal';
import StaticContent from '@midudev/react-static-content';
class GBUIApp extends React.Component {
constructor() {
super();
this.state = {
line: null,
token: null,
instanceClient: null
};
window.user = this.getUser();
}
generateRandomId(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const array = new Uint32Array(length);
window.crypto.getRandomValues(array);
for (let i = 0; i < length; i++) {
result += characters.charAt(array[i] % characters.length);
}
return result;
}
sendToken(token) {
setTimeout(() => {
window.line
.postActivity({
type: 'event',
name: 'updateToken',
data: token,
locale: 'en-us',
textFormat: 'plain',
timestamp: new Date().toISOString(),
from: this.getUser()
})
.subscribe(() => {
window.userAgentApplication.logout();
});
}, 400);
}
send(command) {
window.line.postActivity({
type: 'event',
name: command,
locale: 'en-us',
textFormat: 'plain',
timestamp: new Date().toISOString(),
from: this.getUser()
});
}
getUser() {
return { id: window.userId, name: 'You' };
}
postMessage(value) {
window.line.postActivity({
type: 'message',
text: value,
from: this.getUser()
});
}
configureChat() {
var botId = window.location.href.split('/')[3];
if (botId.indexOf('#') !== -1) {
botId = botId.split('#')[0];
}
if (!botId || botId === '') {
botId = '[default]';
}
fetch('/instances/' + botId)
.then(res => res.json())
.then(
result => {
this.setupBotConnection(result);
},
error => {
this.setState({
isLoaded: false,
err: error
});
}
);
}
authenticate() {
if (this.state.instanceClient.authenticatorClientId === null) {
return;
}
let _this_ = this;
let authority = 'https://login.microsoftonline.com/' + this.state.instanceClient.authenticatorTenant;
let graphScopes = ['Directory.AccessAsUser.All'];
let userAgentApplication = new UserAgentApplication(
this.state.instanceClient.authenticatorClientId,
authority,
function (errorDesc, token, error) {
if (error) {
_this_.sendToken(error);
}
}
);
window.userAgentApplication = userAgentApplication;
if (
!userAgentApplication.isCallback(window.location.hash) &&
window.parent === window &&
!window.opener &&
userAgentApplication.getUser
) {
var user = userAgentApplication.getUser();
if (user) {
userAgentApplication.acquireTokenSilent(graphScopes).then(
function (accessToken) {
_this_.sendToken(accessToken);
},
function (error) {
_this_.sendToken(error);
}
);
}
}
}
setupBotConnection(instanceClient) {
let _this_ = this;
window['botchatDebug'] = true;
window.userId = this.generateRandomId(16);
_this_.setState({ token: instanceClient.webchatToken });
const line = instanceClient.webchatToken
? new DirectLine({
userId:window.userId,
userIdOnStartConversation: window.userId,
token: instanceClient.webchatToken
})
: new DirectLine({
domain: instanceClient.domain,
userId:window.userId,
userIdOnStartConversation: window.userId,
secret: null,
token: null,
webSocket: false // defaults to true
});
line.setUserId(window.userId);
_this_.setState({ line: line });
line.connectionStatus$.subscribe(connectionStatus => {
if (connectionStatus === ConnectionStatus.Online) {
_this_.setState({ instanceClient: instanceClient });
window['botConnection'] = line;
}
});
window.line = line;
line.activity$
.filter(activity => activity.type === 'event' && activity.name === 'loadInstance')
.subscribe(() => {
_this_.send('startGB');
_this_.authenticate();
});
line.activity$
.filter(activity => activity.type === 'event' && activity.name === 'stop')
.subscribe(() => {
if (_this_.player) {
_this_.player.stop();
}
});
line.activity$
.filter(activity => activity.type === 'event' && activity.name === 'play')
.subscribe(activity => {
_this_.setState({ playerType: activity.value.playerType });
_this_.player.play(activity.value.data);
});
}
componentDidMount() {
this.configureChat();
}
webSpeechPonyfillFactory = 0;
render() {
let playerComponent = '';
if (this.state.playerType) {
switch (this.state.playerType) {
case 'markdown':
playerComponent = (
<GBMarkdownPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
case 'bullet':
playerComponent = (
<GBBulletPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
case 'video':
playerComponent = (
<GBVideoPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
case 'url':
playerComponent = (
<GBUrlPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
case 'multiurl':
playerComponent = (
<GBMultiUrlPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
case 'image':
playerComponent = (
<GBImagePlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
/* case 'pbi':
playerComponent = (
<GBPowerBIPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break; */
case 'login':
playerComponent = (
<GBLoginPlayer
app={this}
ref={player => {
this.player = player;
}}
/>
);
break;
default:
playerComponent = <div>GBERROR: Unknow player type specified on message from server.</div>;
break;
}
}
let chat = <div />;
let gbCss = <div />;
let seo = <div />;
let sideBar = <div />;
if (this.state.line) {
chat = (
<ReactWebChat
ref={chat => {
this.chat = chat;
}}
userID= {window.userId}
locale={'en-us'}
directLine={this.state.line}
/>
);
if (this.state.instanceClient) {
gbCss = <GBCss instance={this.state.instanceClient} />;
seo = <SEO instance={this.state.instanceClient} />;
document.body.style.setProperty('background-color', this.state.instanceClient.color2, 'important');
sideBar = (
<div
className="sidebar"
ref={node => {
if (node) {
node.style.setProperty('background-color', this.state.instanceClient.color1, 'important');
}
}}
>
<SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
</div>
);
}
}
return (
<StaticContent>
{seo}
<div>
{gbCss}
{sideBar}
<div className="player">{playerComponent}</div>
<div className="webchat">{chat}</div>
</div>
</StaticContent>
);
}
}
export default GBUIApp;

View file

@ -1,48 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
import React from "react";
import { Chat } from "botframework-webchat";
class ChatPane extends React.Component {
render() {
return (
<Chat
ref={(chat) => { this.chat = chat; }}
botConnection={this.props.botConnection}
user={{ id: "webUser@gb", name: "You" }}
bot={{ id: "bot@gb", name: "Bot" }}
/>
);
}
}
export default ChatPane;

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