Compare commits

..

No commits in common. "main" and "rodrigorodriguez-patch-4" have entirely different histories.

163 changed files with 4751 additions and 51903 deletions

2
.deployment Normal file
View file

@ -0,0 +1,2 @@
[config]
command = bash ./deploy.sh

View file

@ -1,46 +0,0 @@
name: GBCI
run: git config --global http.sslVerify false
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: gbo
steps:
- name: Disable SSL verification (temporary)
run: git config --global http.sslVerify false
- uses: actions/checkout@v4
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '20'
# cache: 'npm'
- name: Copy files to deployment location
run: |
echo "[General Bots Deployer] Copying files to deploy location..."
sudo rm -rf /opt/gbo/bin/bot/botserver/dist
sudo cp -r ./* /opt/gbo/bin/bot/botserver
- name: Building BotServer
run: |
echo "[General Bots Deployer] Building BotServer..."
# rm -rf /opt/gbo/bin/bot/botserver/node_modules
cd /opt/gbo/bin/bot/botserver
sudo npm ci --production
npm run build-server
npm run build-gbui
- name: Restart Bots Deployer
run: |
echo "[General Bots Deployer] Restarting..."
lxc restart pragmatismo-bot

9
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,9 @@
<!-- File a GitHub issue only for bugs or feature requests related to the code **in this repository**. For other topics you can get more information in the README file. -->
### Observed Results:
<!-- This could be a description, error output, steps to reproduce, a feature missed, etc. -->
### Expected behavior:
<!-- What did you expect to happen? -->

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

7
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View file

@ -0,0 +1,7 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
---

View file

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

2
.github/ISSUE_TEMPLATE/requirement vendored Normal file
View file

@ -0,0 +1,2 @@
**Description**
A clear and concise description of what the requirement is.

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,16 @@
### Changes description
<!-- Describe results, user mentions, screenshots, screencast (gif) -->
### Checklist
Please check if your PR fulfills the following specifications:
- [ ] Tests for the changes have been done
- [ ] Docs have been added/updated
### References
<!-- issues related (for reference or to be closed) and/or links of discuss -->
Closes #N/A

3
.github/invite-contributors.yml vendored Normal file
View file

@ -0,0 +1,3 @@
isOutside: true
# Team Name
team: contributors

44
.github/settings.yml vendored Normal file
View file

@ -0,0 +1,44 @@
repository:
name: botserver
description: botserver
homepage: http://pragmatismo.cloud/general-bots
topics: node-module
private: false
has_issues: true
has_wiki: false
has_downloads: true
default_branch: develop
allow_squash_merge: true
allow_merge_commit: false
allow_rebase_merge: true
labels:
- name: bug
color: f44336
- name: build
color: 795548
- name: ci
color: fbca04
- name: documentation
color: 607d8b
- name: duplicate
color: 9e9e9e
- name: feature
color: 3f51b5
- name: invalid
color: cddc39
- name: performance
color: 009688
- name: question
color: ff5722
- name: refactor
color: 9c27b0
- name: style
color: 2196f3
- name: test
color: 8bc34a
- name: wontfix
color: ffffff
- name: help wanted
color: 33aa3f
- name: good first issue
color: 7057ff

3
.gitignore vendored
View file

@ -25,10 +25,9 @@ GB.log
gb.log
GB.log.json
yarn-error.log
package-lock.json
yarn-lock.json
logo.svg
screenshot.png
data.db
.wwebjs_cache
*doula*
*botpoc*

33
.travis.yml Normal file
View file

@ -0,0 +1,33 @@
dist: focal
language: node_js
node_js:
- 19.7.0
notifications:
email: false
before_script:
- npm run build
branches:
only:
- main
- /^greenkeeper/.*$/
except:
- /^v\d+\.\d+\.\d+$/
after_success:
- npm install -g travis-deploy-once
- npm run travis-deploy-once "npm run semantic-release"
- npm pack
deploy:
- provider: pages
skip_cleanup: true
local_dir: docs/reference
github_token: $GITHUB_TOKEN
on:
tags: false
branch: main

30
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,30 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"sourceMaps": true,
"name": "Debug Program",
"runtimeExecutable": "node",
"program": "${workspaceRoot}/boot.mjs",
"cwd": "${workspaceRoot}",
"env": {
"NODE_ENV": "development",
"NODE_NO_WARNINGS": "1"
},
"args": [
"--require", "${workspaceRoot}/suppress-node-warnings.cjs"
],
"skipFiles": [
"node_modules/**/*.js",
"<node_internals>/**"
],
"outFiles": [
"${workspaceRoot}/dist/**/*.js"
],
"stopOnEntry": false,
"console": "integratedTerminal"
}
]
}

4
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"git.ignoreLimitWarning": true,
"cmake.ignoreCMakeListsMissing": true
}

30
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,30 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"problemMatcher": [
"$tsc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View file

@ -32,7 +32,7 @@ In general, things we find useful when reviewing suggestions are:
## Contributing bug fixes
General Bots is current in early preview. We're still accepting contributions in the form of bug fixes.
A bug must have an issue tracking it in the issue tracker that has been approved by the pragmatismo.com.br team. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort.
A bug must have an issue tracking it in the issue tracker that has been approved by the pragmatismo.cloud team. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort.
## Contributing features
@ -40,7 +40,7 @@ Please open an issue with the `Schema` label to get a discussion started.
## Legal
We appreciate community contributions to code repositories open sourced by pragmatismo.com.br. By signing a contributor license agreement, we ensure that the community is free to use your contributions.
We appreciate community contributions to code repositories open sourced by pragmatismo.cloud. By signing a contributor license agreement, we ensure that the community is free to use your contributions.
## Housekeeping

8
FEATURES.md Normal file
View file

@ -0,0 +1,8 @@
# General Bots Features
| Feature | BF | GB |
|----------------------------------------------------------------------------|----|----|
| Use of conversational administration to manage bot packages (Talk to admin)| - | X |
| F5 to run on VSCode | - | X |
| Isolated code on packages | - | X |
| Breaking changes protected | - | X |

View file

@ -1,5 +1,5 @@
General Bots is licensed under a dual license. To check which license
edition of General bots you have installed, please ask info@pragmatismo.com.br
edition of General bots you have installed, please ask info@pragmatismo.cloud
informing your Customer ID.
If you modify this Program, or any covered work, by combining it

7
LOCALIZATION.md Normal file
View file

@ -0,0 +1,7 @@
# Localization in General Bots
## .gbapp
The localization is done by adding a strings.ts file to the root of the .gbapp package.

115
README.md
View file

@ -1,19 +1,16 @@
### Key Facts
- LLM Orchestrator AGPL licensed (to use as custom-label SaaS, contributing back)
- AGPL License (to use as custom-label SaaS)
- True community governance
- No single corporate control
- 5+ years of stability
- Never changed license
- Enterprise-grad
- Hosted locally or Multicloud
## Contributors
<a href="https://github.com/generalbots/botserver/graphs/contributors">
<img src="https://contrib.rocks/image?repo=generalbots/botserver" />
</a>
## Overview
| Area | Status |
|------------------------------|----------------------------------------------------------------------------------------------------|
@ -21,16 +18,17 @@
| Community | [![StackExchange](https://img.shields.io/stackexchange/stackoverflow/t/generalbots.svg)](https://stackoverflow.com/questions/tagged/generalbots) [![Open-source](https://badges.frapsoft.com/os/v2/open-source.svg)](https://badges.frapsoft.com) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![License](https://img.shields.io/badge/license-AGPL-blue.svg)](https://github.com/GeneralBots/BotServer/blob/master/LICENSE.txt)|
| Management | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://gitHub.com/GeneralBots/BotServer/graphs/commit-activity) |
| Security | [![Known Vulnerabilities](https://snyk.io/test/github/GeneralBots/BotServer/badge.svg)](https://snyk.io/test/github/GeneralBots/BotServer) |
| Building & Quality | [![Coverage Status](https://coveralls.io/repos/github/GeneralBots/BotServer/badge.svg)](https://coveralls.io/github/GeneralBots/BotServer) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) |
| Packaging | [![forthebadge](https://badge.fury.io/js/botserver.svg)](https://badge.fury.io) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) |
| Samples | [BASIC](https://github.com/GeneralBots/BotServer/tree/master/packages/default.gbdialog) or [![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/GeneralBots/AzureADPasswordReset.gbapp)
| [Docker Image](https://github.com/lpicanco/docker-botserver) ![Docker Pulls](https://img.shields.io/docker/pulls/lpicanco/botserver.svg) <br/> *Provided by [@lpicanco](https://github.com/lpicanco/docker-botserver)* |
| Building & Quality | [![Build Status](https://travis-ci.com/GeneralBots/BotServer.svg?branch=master)](https://app.travis-ci.com/github/GeneralBots/BotServer) [![Coverage Status](https://coveralls.io/repos/github/GeneralBots/BotServer/badge.svg)](https://coveralls.io/github/GeneralBots/BotServer) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) |
| Packaging | [![forthebadge](https://badge.fury.io/js/botserver.svg)](https://badge.fury.io) [![ZipFile](https://camo.githubusercontent.com/0150c0f148d50fe9750ebc5d313581da699a8c50/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7a69702d646f776e6c6f61642d626c75652e737667)](https://github.com/GeneralBots/BotServer/releases/latest) [![Dependencies](https://david-dm.org/GeneralBots/botserver.svg)](https://david-dm.org) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) |
| Samples | [VBA](https://github.com/GeneralBots/BotServer/tree/master/packages/default.gbdialog) or [![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/GeneralBots/AzureADPasswordReset.gbapp)
| [Docker Image](https://github.com/lpicanco/docker-botserver) | ![Docker Automated build](https://img.shields.io/docker/automated/lpicanco/botserver.svg) ![Docker Build Status](https://img.shields.io/docker/build/lpicanco/botserver.svg) ![MicroBadger Size](https://img.shields.io/microbadger/image-size/lpicanco/botserver.svg) ![MicroBadger Layers](https://img.shields.io/microbadger/layers/lpicanco/botserver.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/lpicanco/botserver.svg) <br/> *Provided by [@lpicanco](https://github.com/lpicanco/docker-botserver)* |
# General Bots
General Bots
------------------
![General Bot Logo](https://github.com/GeneralBots/BotServer/blob/main/logo.png?raw=true))
![General Bot Logo](https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/logo.png)
General Bot is a strongly typed LLM conversational platform 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.
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.
## What is a Bot Server?
@ -44,78 +42,9 @@ advanced fashion writing custom code in editors like Visual Studio Code, Atom or
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). LLM and BASIC can be mixed used to build custom dialogs so Bot can be extended just like VBA for Excel.
editor). BASIC can be used to build custom dialogs so Bot can be extended just like VBA for Excel (currently in alpha).
## Getting Started
### Prerequisites
Before you embark on your General Bots journey, ensure you have the following tools installed:
- **Node.js (version 20 or later)**: General Bots leverages the latest features of Node.js to provide a robust and efficient runtime environment. Download it from [nodejs.org](https://nodejs.org/en/download/).
- **Git (latest stable version)**: Essential for version control and collaborating on bot projects. Get it from [git-scm.com](https://git-scm.com/downloads).
### Quick Start Guide
Follow these steps to get your General Bots server up and running:
1. Clone the repository:
```bash
git clone https://github.com/GeneralBots/BotServer
```
This command creates a local copy of the General Bots server repository on your machine.
2. Navigate to the project directory:
```bash
cd BotServer
```
This changes your current directory to the newly cloned BotServer folder.
3. Install dependencies and start the server:
```bash
npm install
npm run start
```
The `npm install` command installs all necessary dependencies for the project. `npm run start` builds your bot server locally and serves it through a development server.
### Accessing Your Bot
Once the server is running, you can access your bot at `http://localhost:4242/`. This local server allows you to interact with your bot and test its functionality in real-time. If you want to publish
without password, define [ADMIN_OPEN_PUBLISH](https://github.com/GeneralBots/BotBook/master/docs/chapter-07-gbot-reference#enviroment-variables-reference) as true in BotServer .env file.
To publish bot packages and initiate a conversation with the bot, use the command:
```
/publish
```
This command prepares your bot packages for use and allows you to start interacting with your bot immediately.
## Development Workflow
### 1. Project Structure
The General Bots server follows a modular architecture designed for flexibility and scalability. Here's an overview of the main directories:
```
BotServer/
├── packages/
│ ├── core.gbapp/ # Core bot functionality
│ ├── kb.gbapp/ # Knowledge base packages
├── src / # Main entry point
└── package.json # Project configuration
```
This structure allows for easy navigation and management of different aspects of your bot project.
### 2. Creating Custom Packages
One of the strengths of General Bots is its extensibility. You can create custom packages to enhance your bot's capabilities:
- **.gbkb (Knowledge Base packages)**: Store and manage your bot's knowledge and responses.
- **.gbtheme (Theme packages)**: Customize the visual appearance of your bot interface.
- **.gbapp (Application packages)**: Add new features and functionalities to your bot.
Each package type has its own structure and purpose, which we'll explore in depth in their respective chapters.
![General Bot Reference Architecture](https://raw.githubusercontent.com/GeneralBots/BotBook/master/images/general-bots-reference-architecture.png)
## Samples
@ -147,12 +76,18 @@ SEND FILE img
## Guide
[Read the General Bots BotBook Guide](https://docs.pragmatismo.com.br)
[Read the General Bots BotBook Guide](https://github.com/GeneralBots/BotBook/tree/master/book).
# Videos
7 AI General Bots LLM Templates for Goodness
[https://www.youtube.com/watch?v=KJgvUPXi3Fw](https://www.youtube.com/watch?v=KJgvUPXi3Fw)
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.
[![General Bot Video](https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/docs/images/video-01-thumb.jpg)](https://www.youtube.com/watch?v=AfKTwljoMOs)
See how easy is to use 'hear' and 'talk' to build Microsoft BOT Framework v4 logic with plain BASIC * published on December 3rd, 2018.
[![See how easy is to use 'hear' and 'talk' to build Microsoft BOT Framework v4 logic with plain BASIC](https://raw.githubusercontent.com/pragmatismo-io/BotServer/master/docs/images/video-02-thumb.jpg)](https://www.youtube.com/watch?v=yX1sF9n9628)
# Contributing
@ -161,14 +96,14 @@ See our [Contribution Guidelines](https://github.com/pragmatismo-io/BotServer/bl
# Reporting Security Issues
Security issues and bugs should be reported privately, via email, to the pragmatismo.com.br Security
team at [security@pragmatismo.com.br](mailto:security@pragmatismo.com.br). You should
Security issues and bugs should be reported privately, via email, to the pragmatismo.cloud Security
team at [security@pragmatismo.cloud](mailto:security@pragmatismo.cloud). 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.
# License & Warranty
General Bot Copyright (c) pragmatismo.com.br. All rights reserved.
General Bot Copyright (c) pragmatismo.cloud. All rights reserved.
Licensed under the AGPL-3.0.
According to our dual licensing model, this program can be used either
@ -184,7 +119,7 @@ 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 Bot" is a registered trademark of pragmatismo.com.br.
"General Bot" is a registered trademark of pragmatismo.cloud.
The licensing of the program under the AGPLv3 does not imply a
trademark license. Therefore any rights, title and interest in
our trademarks remain entirely with us.

6
ROADMAP.md Normal file
View file

@ -0,0 +1,6 @@
# Roadmap
| Title | Priority | Release | Status |
|-------------------------------|------------------------------------------------------------------------------------------------------------|---------|--------|
| Isolation of .gbapp per .gbot | Today .gbapp loaded is shared across all bot instances and must be associated to one or more individually. | Medium | 2019Q4 |
| Python based .gbapps | Write conversational login in Python | Low | - |

7
SAMPLES.md Normal file
View file

@ -0,0 +1,7 @@
# General Bots Server Samples
| Sample | Description |
|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [IntranetBotQuickStart-gbai](https://github.com/pragmatismo-io/IntranetBotQuickStart.gbai) | Free modules from General Bots Intranet based bot. |
| [AzureADPasswordReset-gbapp](https://github.com/pragmatismo-io/AzureADPasswordReset.gbapp) | Custom General Bot App (gbapp) for resetting an user password in Azure Active Directory, Office 365, Dynamics 365 or any app published through Azure AD. |
| [ProjectOnline.gbkb](https://github.com/pragmatismo-io/ProjectOnline.gbkb) | The .gbkb file demonstring a Knowledge Base for pragmatismo.cloud KBot for Microsoft Project. |

View file

@ -20,7 +20,7 @@ Review process
We will review this policy yearly.
In the meantime, if you have any questions, suggestions
or feedback, please contact security@pragmatismo.com.br
or feedback, please contact security@pragmatismo.cloud
We will only classify information which is necessary for the completion of our duties. We will also limit
@ -58,4 +58,4 @@ any other tasks that could result in a conflict of interest.
## Reporting a Vulnerability
You can expect to get an update on a reported vulnerability in a day or two.
security@pragmatismo.com.br
security@pragmatismo.cloud

View file

@ -7,7 +7,7 @@
},
"servers": [
{
"url": "https://generalbots.online/api",
"url": "https://gb.pragmatismo.cloud/api",
"description": "General Bots Online"
}
]

View file

@ -1,6 +1,6 @@
#!/usr/bin/env node
process.stdout.write(`General Bots 5 VM: node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
process.stdout.write(`General Bots VM: node@${process.version.replace('v', '')}, ${process.platform} ${process.arch} `);
import fs from 'fs/promises';
import os from 'node:os';

10
greenkeeper.json Normal file
View file

@ -0,0 +1,10 @@
{
"groups": {
"default": {
"packages": [
"package.json",
"packages/default.gbui/package.json"
]
}
}
}

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

48942
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -102,7 +102,7 @@
"@semantic-release/git": "10.0.1",
"@sendgrid/mail": "8.1.3",
"@sequelize/core": "7.0.0-alpha.37",
"@sequelize/postgres": "^7.0.0-alpha.43",
"@types/node": "22.5.2",
"@types/validator": "13.12.1",
"adm-zip": "0.5.16",
"ai2html": "^0.121.1",
@ -113,7 +113,6 @@
"async-promises": "0.2.3",
"async-retry": "1.3.3",
"basic-auth": "2.0.1",
"bcrypt": "^5.1.1",
"billboard.js": "3.13.0",
"bluebird": "3.7.2",
"body-parser": "1.20.2",
@ -170,13 +169,11 @@
"langchain": "0.2.17",
"language-tags": "1.0.9",
"line-replace": "2.0.1",
"livekit-server-sdk": "^2.12.0",
"lodash": "4.17.21",
"luxon": "3.5.0",
"mammoth": "1.8.0",
"mariadb": "3.3.1",
"mime-types": "2.1.35",
"minio": "^8.0.4",
"moment": "2.30.1",
"ms-rest-azure": "3.0.2",
"mysql": "^2.18.1",
@ -186,7 +183,6 @@
"node-html-parser": "6.1.13",
"node-nlp": "4.27.0",
"node-tesseract-ocr": "2.2.1",
"nodemailer": "6.10.1",
"nodemon": "^3.1.7",
"npm": "10.8.3",
"open": "10.1.0",
@ -196,7 +192,7 @@
"pdf-parse": "1.1.1",
"pdf-to-png-converter": "3.3.0",
"pdfjs-dist": "4.6.82",
"pg": "^8.13.1",
"pdfkit": "0.15.0",
"phone": "3.1.50",
"pizzip": "3.1.7",
"pptxtemplater": "1.0.5",
@ -225,9 +221,6 @@
"sqlite3": "5.1.7",
"ssr-for-bots": "1.0.1-c",
"strict-password-generator": "1.1.2",
"stripe": "^18.0.0",
"super-strong-password-generator": "2.0.2",
"super-strong-password-generator-es": "2.0.2",
"svg2img": "^1.0.0-beta.2",
"swagger-client": "3.29.2",
"swagger-ui-dist": "5.17.14",
@ -255,9 +248,6 @@
"zod-to-json-schema": "3.23.2"
},
"devDependencies": {
"@types/lodash": "^4.17.20",
"@types/node": "^24.1.0",
"@types/node-fetch": "^2.6.12",
"@types/qrcode": "1.5.5",
"@types/url-join": "4.0.3",
"@typescript-eslint/eslint-plugin": "8.4.0",
@ -271,6 +261,8 @@
"prettier-standard": "16.4.1",
"semantic-release": "24.1.0",
"simple-commit-message": "4.1.3",
"super-strong-password-generator": "2.0.2",
"super-strong-password-generator-es": "2.0.2",
"travis-deploy-once": "5.0.11",
"tslint": "6.1.3",
"tsx": "^4.19.1",

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -47,7 +47,6 @@ 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 {
@ -87,7 +86,7 @@ class AdminDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (await GBUtil.comparePassword( sensitive, min.instance.adminPass)) {
if (sensitive === min.instance.adminPass) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await step.endDialog(true);
@ -121,7 +120,7 @@ class AdminDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (await GBUtil.comparePassword( sensitive, min.instance.adminPass)) {
if (sensitive === min.instance.adminPass) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await min.conversationalService.prompt(min, step, Messages[locale].which_task);
@ -293,8 +292,6 @@ class AdminDialog extends IGBDialog {
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;
@ -307,8 +304,6 @@ class AdminDialog extends IGBDialog {
if (
packageName.toLowerCase() === 'gbdialog' ||
packageName.toLowerCase() === 'gbdrive' ||
packageName.toLowerCase() === 'gbdata' ||
packageName.toLowerCase() === 'gbkb' ||
packageName.toLowerCase() === 'gbot' ||
packageName.toLowerCase() === 'gbtheme'

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -168,7 +168,7 @@ export class GBAdminService implements IGBAdminService {
) {
const packageName = text.split(' ')[1];
const folderName = text.substring(text.indexOf(packageName) + packageName.length + 1);
const folderName = text.split(' ')[2];
const packageType = path.extname(folderName).substr(1);
const gbaiPath = GBUtil.getGBAIPath(min.instance.botId, packageType, null);
const localFolder = path.join('work', gbaiPath);

View file

@ -1,7 +1,7 @@
export const Messages = {
'en-US': {
authenticate: 'Please, authenticate:',
welcome: 'Welcome to pragmatismo.com.br GeneralBots Administration.',
welcome: 'Welcome to pragmatismo.cloud GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: command => `I'm working on ${command}...`,
finished_working: 'Done.',
@ -26,7 +26,7 @@ export const Messages = {
},
'pt-BR': {
authenticate: 'Please, authenticate:',
welcome: 'Welcome to pragmatismo.com.br GeneralBots Administration.',
welcome: 'Welcome to pragmatismo.cloud GeneralBots Administration.',
which_task: 'Which task do you wanna run now?',
working: command => `I'm working on ${command}...`,
finished_working: 'Done.',

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -111,7 +111,7 @@ export class StartDialog {
instance.cloudLocation = location;
instance.marketplaceId = appId;
instance.marketplacePassword = appPassword;
instance.adminPass = await GBUtil.hashPassword(GBAdminService.getRndPassword());
instance.adminPass = GBAdminService.getRndPassword();
return { instance, credentials, subscriptionId , installationDeployer};
}

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -518,7 +518,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
instance.nlpAuthoringKey = authoringKey;
instance.marketplaceId = appId;
instance.marketplacePassword = appPassword;
instance.adminPass = await GBUtil.hashPassword(GBAdminService.getRndPassword());
instance.adminPass = GBAdminService.getRndPassword();
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
// tslint:disable-next-line:no-http-string
@ -986,6 +986,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
appSettings: [
{ name: 'WEBSITES_CONTAINER_START_TIME_LIMIT', value: `${WebSiteResponseTimeout}` },
{ name: 'WEBSITE_NODE_DEFAULT_VERSION', value: await GBAdminService.getNodeVersion() },
{ name: 'ADMIN_PASS', value: `${instance.adminPass}` },
{ name: 'BOT_ID', value: `${instance.botId}` },
{ name: 'CLOUD_SUBSCRIPTIONID', value: `${instance.cloudSubscriptionId}` },
{ name: 'CLOUD_LOCATION', value: `${instance.cloudLocation}` },

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -30,6 +30,7 @@
'use strict';
import sgMail from '@sendgrid/mail';
import { ActivityTypes } from 'botbuilder';
import { GBLog, GBMinInstance } from 'botlib';
import * as df from 'date-diff';
@ -42,7 +43,7 @@ import mime from 'mime-types';
import tesseract from 'node-tesseract-ocr';
import path from 'path';
import { CollectionUtil } from 'pragmatismo-io-framework';
import puppeteer, { executablePath } from 'puppeteer';
import puppeteer from 'puppeteer';
import qrcode from 'qrcode';
import urlJoin from 'url-join';
import pkg from 'whatsapp-web.js';
@ -60,8 +61,6 @@ import { ChartServices } from './ChartServices.js';
import { GBVMService } from './GBVMService.js';
import { SystemKeywords } from './SystemKeywords.js';
import { WebAutomationServices } from './WebAutomationServices.js';
import { Client } from 'minio';
import nodemailer from 'nodemailer';
const { List, Buttons } = pkg;
@ -94,11 +93,7 @@ export class DialogKeywords {
// Launch Puppeteer to render the chart
const browser = await puppeteer.launch({
headless: process.env.CHROME_HEADLESS === 'true',
executablePath: process.env.CHROME_PATH ? process.env.CHROME_PATH : executablePath(),
}
);
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Load Billboard.js styles and scripts
@ -578,6 +573,7 @@ export class DialogKeywords {
// tslint:disable-next-line:no-console
GBLogEx.info(min, `[E-mail]: to:${to},subject: ${subject},body: ${body}.`);
const emailToken = process.env.EMAIL_API_KEY;
// Inline word document used as e-mail body.
@ -586,36 +582,24 @@ export class DialogKeywords {
body = result.value;
}
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SERVER || 'localhost',
port: parseInt(process.env.EMAIL_PORT || '587', 10),
secure: false,
auth: {
user: process.env.EMAIL_USER ,
pass: process.env.EMAIL_PASS ,
},
tls: {
rejectUnauthorized: (process.env.EMAIL_REJECT_UNAUTHORIZED === 'true'),
},
});
const mailOptions = {
from: process.env.EMAIL_FROM,
to: to,
subject: subject,
text: body,
html: body,
// headers: {
// 'List-Unsubscribe': `<mailto:${config.unsubscribeEmail}?subject=Unsubscribe>`,
// 'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
// }
if (emailToken) {
return new Promise<any>((resolve, reject) => {
sgMail.setApiKey(emailToken);
const msg = {
to: to,
from: process.env.EMAIL_FROM,
subject: subject,
text: body,
html: body
};
await transporter.sendMail(mailOptions);
GBLogEx.info(min, `E-mail ${to} (${subject}) sent via NodeMailer.`);
sgMail.send(msg, false, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
} else {
let { client } = await GBDeployer.internalGetDriveClient(min);
@ -725,9 +709,8 @@ export class DialogKeywords {
proc.roles = role;
// Checks access.
const file = process.env.GB_MODE === 'legacy' ? 'People.xlsx' : 'people.csv';
const filters = [file, `${role}=x`, `id=${user.userSystemId}`];
const filters = ['People.xlsx', `${role}=x`, `id=${user.userSystemId}`];
const people = await sys.find({ pid, handle: null, args: filters });
if (!people) {
@ -1104,7 +1087,7 @@ export class DialogKeywords {
// In case of unmatch, asks the person to try again.
if (result === null) {
await this.talk({ pid, text: `Digite por favor um dos itens sugeridos ou uma parte do texto.` });
await this.talk({ pid, text: `Escolha por favor um dos itens sugeridos.` });
return await this.hear({ pid, kind, args });
}
} else if (kind === 'file') {
@ -1409,7 +1392,7 @@ export class DialogKeywords {
const client = await GBUtil.getDirectLineClient(min);
conversation.client = client;
const response = await client.apis.Conversations.Conversations_StartConversation(
);
conversation.conversationId = response.obj.conversationId;
@ -1512,7 +1495,7 @@ export class DialogKeywords {
else {
if (GBConfigService.get('GB_MODE') === 'legacy') {
if (GBConfigService.get('STORAGE_NAME')) {
const ext = path.extname(filename);
const gbaiName = GBUtil.getGBAIPath(min.botId);
@ -1537,52 +1520,17 @@ export class DialogKeywords {
'cache',
`${fileOnly.replace(/\s/gi, '')}-${GBAdminService.getNumberIdentifier()}.${ext}`
);
await fs.writeFile(localName, new Uint8Array(buf), { encoding: null });
await fs.writeFile(localName, buf, { encoding: null });
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
} else if (GBConfigService.get('GB_MODE') === 'gbcluster') {
const ext = path.extname(filename);
const fileUrl = urlJoin('/', `${min.botId}.gbdrive`, filename);
GBLogEx.info(min, `Direct send from .gbdrive: ${fileUrl} to ${mobile}.`);
const fileOnly = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost',
port: parseInt(process.env.DRIVE_PORT || '9000', 10),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET,
});
const bucketName = (process.env.DRIVE_ORG_PREFIX + min.botId + '.gbai').toLowerCase();
localName = path.join(
'work',
gbaiName,
'cache',
`${GBAdminService.getNumberIdentifier()}-${fileOnly}`
);
await minioClient.fGetObject(bucketName, fileUrl, localName);
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
GBLogEx.info(min, `Exposing ${localName} to ${url}...`);
}
else {
const gbdriveName = GBUtil.getGBAIPath(min.botId, 'gbdrive');
localName = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbdriveName, filename);
}
if (localName.endsWith('.image.pdf')) {
GBLogEx.info(min, `Converting PDF to images...`);
if (localName.endsWith('.pdf')) {
const pngs = await GBUtil.pdfPageAsImage(min, localName, undefined);
await CollectionUtil.asyncForEach(pngs, async png => {
@ -1600,11 +1548,8 @@ export class DialogKeywords {
contentUrl: url
});
if (!isNaN(mobile)) {
await min.whatsAppDirectLine.sendFileToDevice(mobile, url, filename, caption, undefined, true, true);
await min.whatsAppDirectLine.sendFileToDevice(mobile, url, filename, caption, undefined, true);
} else {
await min.conversationalService['sendOnConversation'](min, user, reply);
}
@ -1623,10 +1568,8 @@ export class DialogKeywords {
const buf = await fs.readFile(filename);
const gbaiName = GBUtil.getGBAIPath(min.botId);
const localName = path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.${ext}`);
await fs.writeFile(localName, buf, { encoding: null });
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
await fs.writeFile(localName, new Uint8Array(buf), { encoding: null });
}
const contentType = mime.lookup(url);
@ -1638,8 +1581,8 @@ export class DialogKeywords {
contentUrl: url
});
if (!isNaN(mobile)) {
await min.whatsAppDirectLine.sendFileToDevice(mobile, url, filename, caption, undefined, false);
if (channel === 'omnichannel' || !user) {
await min.conversationalService.sendFile(min, null, mobile, url, caption);
} else {
await min.conversationalService['sendOnConversation'](min, user, reply);
}

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -39,7 +39,6 @@ import { CollectionUtil } from 'pragmatismo-io-framework';
import { ScheduleServices } from './ScheduleServices.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import urlJoin from 'url-join';
import { PostgresDialect } from '@sequelize/postgres';
import { NodeVM, VMScript } from 'vm2';
import { createVm2Pool } from './vm2-process/index.js';
import { watch } from 'fs';
@ -150,7 +149,7 @@ export class GBVMService extends GBService {
let mainName = GBVMService.getMethodNameFromVBSFilename(filename);
min.scriptMap[filename] = mainName;
if (writeVBS && GBConfigService.get('GB_MODE') === 'legacy') {
if (writeVBS && GBConfigService.get('STORAGE_NAME')) {
let text = await this.getTextFromWord(folder, wordFile);
// Write VBS file without pragma keywords.
@ -205,7 +204,7 @@ export class GBVMService extends GBService {
"author": "${min.botId} owner.",
"license": "ISC",
"dependencies": {
"yaml": "2.4.2",
"encoding": "0.1.13",
"isomorphic-fetch": "3.0.0",
"punycode": "2.1.1",
@ -245,133 +244,42 @@ export class GBVMService extends GBService {
const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
GBLogEx.info(min, str);
}
GBLogEx.info(min, str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
const acquire = parseInt(GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT'));
let sequelizeOptions;
// Simple function to convert all object keys to lowercase
const toLowerCase = (obj) => {
if (!obj) return obj;
if (typeof obj !== 'object') return obj;
return Object.keys(obj).reduce((acc, key) => {
acc[key.toLowerCase()] = obj[key];
return acc;
}, {});
}
if (dialect === 'postgres') {
sequelizeOptions = {
host: host,
port: port,
logging: logging as boolean,
dialect: dialect,
dialectOptions: {
ssl: false,
application_name: 'General Bots',
connectTimeout: 10000,
query_timeout: 10000,
statement_timeout: 10000,
idle_in_transaction_session_timeout: 10000,
},
pool: {
max: 1,
min: 0,
idle: 10000,
evict: 10000,
acquire: acquire
},
define: {
// Disable timestamps globally
timestamps: false,
// Prevent createdAt/updatedAt from being selected
defaultScope: {
attributes: {
exclude: ['createdAt', 'updatedAt']
}
},
// Convert all table names to lowercase
freezeTableName: true,
hooks: {
beforeSave: (options) => {
if (options.where) {
options.where = toLowerCase(options.where);
}
},
beforeDestroy: (options) => {
if (options.where) {
options.where = toLowerCase(options.where);
}
},
beforeFind: (options) => {
if (options.where) {
options.where = toLowerCase(options.where);
}
},
beforeDefine: (attributes, options) => {
// Convert model name and table name to lowercase
if (options.modelName) {
options.modelName = options.modelName.toLowerCase();
}
if (options.tableName) {
options.tableName = options.tableName.toLowerCase();
} else {
options.tableName = options.modelName.toLowerCase();
} for (const attr in attributes) {
const lowered = attr.toLowerCase();
if (attr !== lowered) {
attributes[lowered] = attributes[attr];
delete attributes[attr];
}
}
}
}
},
};
}
else {
sequelizeOptions = {
define: {
charset: 'utf8',
collate: 'utf8_general_ci',
freezeTableName: true,
timestamps: false
},
host: host,
port: port,
logging: logging as boolean,
dialect: dialect,
quoteIdentifiers: false, // set case-insensitive
dialectOptions: {
options: {
trustServerCertificate: true,
encrypt: encrypt,
requestTimeout: 120 * 1000
}
},
pool: {
max: 1,
min: 0,
idle: 10000,
evict: 10000,
acquire: acquire
const sequelizeOptions = {
define: {
charset: 'utf8',
collate: 'utf8_general_ci',
freezeTableName: true,
timestamps: false
},
host: host,
port: port,
logging: logging as boolean,
dialect: dialect,
quoteIdentifiers: false, // set case-insensitive
dialectOptions: {
options: {
trustServerCertificate: true,
encrypt: encrypt,
requestTimeout: 120 * 1000
}
};
}
},
pool: {
max: 5,
min: 0,
idle: 10000,
evict: 10000,
acquire: acquire
}
};
if (!min[connectionName]) {
GBLogEx.info(min, `Loading data connection ${connectionName} (${dialect})...`);
GBLogEx.info(min, `Loading data connection ${connectionName}...`);
min[connectionName] = new Sequelize(storageName, username, password, sequelizeOptions);
min[connectionName]['gbconnection'] = con;
}
@ -507,10 +415,10 @@ export class GBVMService extends GBService {
});
sync = sync ? sync : !found;
// Do not erase tables in case of an error in collection retrieval.
if (tables.length === 0) {
if (tables.length === 0){
sync = false;
}
@ -604,13 +512,13 @@ export class GBVMService extends GBService {
const jsfile: string = `${filename}.js`;
const template = (await fs.readFile('./vm-inject.js')).toString();
code = template.replace('//##INJECTED_CODE_HERE', code);
code = code.replace('//##INJECTED_HEADER', `port=${GBVMService.API_PORT}; botId='${min.botId}';`);
code = template.replace('//##INJECTED_CODE_HERE', code );
code = code.replace('//##INJECTED_HEADER', `port=${GBVMService.API_PORT}; botId='${min.botId}';` );
code = ji.default(code, ' ');
await fs.writeFile(jsfile, code);
}
private async executeTasks(min, tasks) {
@ -809,13 +717,7 @@ export class GBVMService extends GBService {
if (!table && !talk && !systemPrompt) {
for (let j = 0; j < keywords.length; j++) {
const oldLine = line;
line = line.replace(keywords[j][0], keywords[j][1]);
if(line != oldLine){
break;
}
line = line.replace(keywords[j][0], keywords[j][1]); // TODO: Investigate delay here.
}
}
@ -975,7 +877,7 @@ export class GBVMService extends GBService {
// Auto-NLP generates BASIC variables related to entities.
if (step?.context?.activity.originalText && min['nerEngine']) {
if (step?.context?.activity.originalText && min['nerEngine']) {
const result = await min['nerEngine'].process(step.context.activity.originalText);
for (let i = 0; i < result.entities.length; i++) {
@ -1034,12 +936,12 @@ export class GBVMService extends GBService {
let result;
try {
if (GBConfigService.get('GBVM') !== false) {
if (!GBConfigService.get('GBVM')) {
return await (async () => {
return await new Promise((resolve) => {
sandbox['resolve'] = resolve;
// TODO: #411 sandbox['reject'] = reject;
sandbox['reject'] = () => { };
sandbox['reject'] = () => {};
const vm1 = new NodeVM({
allowAsync: true,

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -113,7 +113,7 @@ export class ImageProcessingServices {
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
@ -124,17 +124,16 @@ export class ImageProcessingServices {
apiVersion: azureOpenAIVersion,
apiKey: azureOpenAIKey
});
// Make a request to the image generation endpoint
const response = await client.images.generate({
model: '',
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
n: 1,
size: '1024x1024'
});
const gbaiName = GBUtil.getGBAIPath(min.botId);
const localName = path.join('work', gbaiName, 'cache', `DALL-E${GBAdminService.getRndReadableIdentifier()}.png`);

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -522,14 +522,6 @@ export class KeywordsExpressions {
}
];
keywords[i++] = [
/^\s*(LOG)(\s*)(.*)/gim,
($0, $1, $2, $3) => {
const params = this.getParams($3, ['obj']);
return `await sys.log ({pid: pid, ${params}})`;
}
];
keywords[i++] = [
/^\s*(.*)\=\s*(DIR)(\s*)(.*)/gim,
($0, $1, $2, $3, $4) => {
@ -580,14 +572,6 @@ export class KeywordsExpressions {
}
];
keywords[i++] = [
/^\s*hear (\w+\$*) as\s*number/gim,
($0, $1) => {
return `${$1} = await dk.hear({pid: pid, kind:"integer"})`;
}
];
keywords[i++] = [
/^\s*hear (\w+\$*) as\s*integer/gim,
($0, $1) => {
@ -922,15 +906,6 @@ export class KeywordsExpressions {
}
];
keywords[i++] = [
/^\s*(POST TO INSTAGRAM)(\s*)(.*)/gim,
($0, $1, $2, $3) => {
const params = this.getParams($3, ['username', 'password', 'imagePath', 'caption']);
return `await sys.postToInstagram ({pid: pid, ${params}})`;
}
];
keywords[i++] = [
/^\s*((?:[a-z]+.?)(?:(?:\w+).)(?:\w+)*)\s*=\s*(datediff)(\s*)(.*)/gim,
($0, $1, $2, $3, $4) => {
@ -1312,7 +1287,7 @@ export class KeywordsExpressions {
keywords[i++] = [
/^\s*((?:[a-z]+.?)(?:(?:\w+).)(?:\w+)*)\s*=\s*(.*)\s*as\s*pdf/gim,
($0, $1, $2) => {
return `${$1} = await sys.asPdf({pid: pid, data: ${$2}})`;
return `${$1} = await sys.asPdf({pid: pid, data: ${$2})`;
}
];
@ -1324,13 +1299,13 @@ export class KeywordsExpressions {
];
keywords[i++] = [
/^\s*(save)(\s*)(.*\.(xlsx|csv))(.*)/gim,
($0, $1, $2, $3, $4, $5) => {
/^\s*(save)(\s*)(.*\.xlsx)(.*)/gim,
($0, $1, $2, $3, $4) => {
$3 = $3.replace(/\'/g, '');
$3 = $3.replace(/\"/g, '');
$3 = $3.replace(/\`/g, '');
$5 = $5.substr(2);
return `await sys.save({pid: pid, file: "${$3}", args: [${$5}]})`;
$4 = $4.substr(2);
return `await sys.save({pid: pid, file: "${$3}", args: [${$4}]})`;
}
];

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -29,53 +29,49 @@
\*****************************************************************************/
'use strict';
import ComputerVisionClient from '@azure/cognitiveservices-computervision';
import ApiKeyCredentials from '@azure/ms-rest-js';
import { BlobServiceClient, BlockBlobClient, StorageSharedKeyCredential } from '@azure/storage-blob';
import { DataTypes, Sequelize } from '@sequelize/core';
import ai2html from 'ai2html';
import alasql from 'alasql';
import retry from 'async-retry';
import { GBLog } from 'botlib';
import csvdb from 'csv-database';
import Docxtemplater from 'docxtemplater';
import Excel from 'exceljs';
import { Page } from 'facebook-nodejs-business-sdk';
import fs from 'fs/promises';
import { IgApiClient } from 'instagram-private-api';
import { BufferWindowMemory } from 'langchain/memory';
import _ from 'lodash';
import mime from 'mime-types';
import ImageModule from 'open-docxtemplater-image-module';
import path from 'path';
import { pdfToPng, PngPageOutput } from 'pdf-to-png-converter';
import PizZip from 'pizzip';
import pptxTemplaterModule from 'pptxtemplater';
import { CollectionUtil } from 'pragmatismo-io-framework';
import urlJoin from 'url-join';
import { setFlagsFromString } from 'v8';
import { runInNewContext } from 'vm';
import { GBServer } from '../../../src/app.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { IgApiClient } from 'instagram-private-api';
import { readFile } from 'fs';
import ai2html from 'ai2html';
import path, { resolve } from 'path';
import { GBLog, GBMinInstance } from 'botlib';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import { ChatServices } from '../../llm.gblib/services/ChatServices.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { DialogKeywords } from './DialogKeywords.js';
import { GBServer } from '../../../src/app.js';
import { GBVMService } from './GBVMService.js';
import { KeywordsExpressions } from './KeywordsExpressions.js';
import fs from 'fs/promises';
import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import urlJoin from 'url-join';
import Excel from 'exceljs';
import { BufferWindowMemory } from 'langchain/memory';
import csvdb from 'csv-database';
import { Sequelize, QueryTypes, DataTypes } from '@sequelize/core';
import packagePath from 'path';
import ComputerVisionClient from '@azure/cognitiveservices-computervision';
import ApiKeyCredentials from '@azure/ms-rest-js';
import alasql from 'alasql';
import PizZip from 'pizzip';
import Docxtemplater from 'docxtemplater';
import pptxTemplaterModule from 'pptxtemplater';
import _ from 'lodash';
import { pdfToPng, PngPageOutput } from 'pdf-to-png-converter';
import ImageModule from 'open-docxtemplater-image-module';
import { GBConversationalService } from '../../core.gbapp/services/GBConversationalService.js';
import { WebAutomationServices } from './WebAutomationServices.js';
import { exec } from 'child_process';
import util from 'util';
// Promisify the exec function for async/await usage
const execPromise = util.promisify(exec);
import { KeywordsExpressions } from './KeywordsExpressions.js';
import { ChatServices } from '../../llm.gblib/services/ChatServices.js';
import mime from 'mime-types';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import retry from 'async-retry';
import { BlobServiceClient, BlockBlobClient, StorageSharedKeyCredential } from '@azure/storage-blob';
import { Page } from 'facebook-nodejs-business-sdk';
import { md5 } from 'js-md5';
import { Client } from 'minio';
import { GBUtil } from '../../../src/util.js';
/**
@ -177,22 +173,22 @@ export class SystemKeywords {
if (date) {
return array
? array.sort((a, b) => {
const c = new Date(a[memberName]);
const d = new Date(b[memberName]);
return c.getTime() - d.getTime();
})
const c = new Date(a[memberName]);
const d = new Date(b[memberName]);
return c.getTime() - d.getTime();
})
: null;
} else {
return array
? array.sort((a, b) => {
if (a[memberName] < b[memberName]) {
return -1;
}
if (a[memberName] > b[memberName]) {
return 1;
}
return 0;
})
if (a[memberName] < b[memberName]) {
return -1;
}
if (a[memberName] > b[memberName]) {
return 1;
}
return 0;
})
: array;
}
}
@ -366,42 +362,8 @@ export class SystemKeywords {
);
}
private async convertWithLibreOffice(pid, inputPath) {
const { min } = await DialogKeywords.getProcessInfo(pid);
const gbaiName = GBUtil.getGBAIPath(min.botId);
const localName = path.join('work', gbaiName, 'cache', `img${GBAdminService.getRndReadableIdentifier()}.pdf`);
try {
// LibreOffice command for conversion using localName as output
const command = `libreoffice --headless --convert-to pdf --outdir "${path.dirname(localName)}" "${inputPath}"`;
GBLogEx.info(min, `Executing: ${command}`);
const { stdout, stderr } = await execPromise(command);
if (stderr) {
GBLogEx.error(min, `LibreOffice stderr: ${stderr}`);
}
GBLogEx.info(min, `LibreOffice stdout: ${stdout}`);
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
return { localName, url };
} catch (error) {
GBLogEx.error(min, `Error converting file to PDF: ${error}`);
throw new Error('PDF conversion failed');
}
}
public async asPdf({ pid, data }) {
let file;
if (data.url) {
file = await this.convertWithLibreOffice(pid, data.localName);
} else {
file = await this.renderTable(pid, data, true, false);
}
public async asPDF({ pid, data }) {
let file = await this.renderTable(pid, data, true, false);
return file;
}
@ -429,7 +391,7 @@ export class SystemKeywords {
return new Promise((resolve, reject) => {
stream.on('data', chunk => chunks.push(chunk));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks.map(chunk => new Uint8Array(chunk)))));
stream.on('end', () => resolve(Buffer.concat(chunks)));
});
};
@ -453,7 +415,7 @@ export class SystemKeywords {
const buffer = pngPages[0].content;
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
await fs.writeFile(localName, new Uint8Array(buffer), { encoding: null });
await fs.writeFile(localName, buffer, { encoding: null });
return { localName: localName, url: url, data: buffer };
}
@ -746,7 +708,7 @@ export class SystemKeywords {
// Writes it to disk and calculate hash.
const data = await response.arrayBuffer();
await fs.writeFile(localName, new Uint8Array(Buffer.from(data)), { encoding: null });
await fs.writeFile(localName, Buffer.from(data), { encoding: null });
const hash = new Uint8Array(md5.array(data));
// Performs uploading passing local hash.
@ -823,7 +785,7 @@ export class SystemKeywords {
const minRef = min;
await retry(
async bail => {
async (bail) => {
const t = this.getTableFromName(tableName, minRef);
try {
await t.bulkCreate(rowsDest);
@ -875,13 +837,7 @@ export class SystemKeywords {
/**
* Saves the content of several variables to a new row in a tabular file.
*
* @example SAVE "customers.csv", name, email, phone, address, city, state, country
*
*/
/**
* Saves the content of several variables to a new row in a tabular file.
*
* @example SAVE "customers.csv", id, name, email, phone
* @example SAVE "customers.xlsx", name, email, phone, address, city, state, country
*
*/
public async save({ pid, file, args }): Promise<any> {
@ -891,89 +847,6 @@ export class SystemKeywords {
const { min } = await DialogKeywords.getProcessInfo(pid);
GBLogEx.info(min, `Saving '${file}' (SAVE). Args: ${args.join(',')}.`);
// Handle gbcluster mode with Minio storage
if (GBConfigService.get('GB_MODE') === 'gbcluster') {
const fileUrl = urlJoin('/', `${min.botId}.gbdata`, file);
GBLogEx.info(min, `Direct data from .csv: ${fileUrl}.`);
const fileOnly = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost',
port: parseInt(process.env.DRIVE_PORT || '9000', 10),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET
});
const gbaiName = GBUtil.getGBAIPath(min.botId);
const bucketName = (process.env.DRIVE_ORG_PREFIX + min.botId + '.gbai').toLowerCase();
const localName = path.join(
'work',
gbaiName,
'cache',
`${fileOnly.replace(/\s/gi, '')}-${GBAdminService.getNumberIdentifier()}.csv`
);
try {
// Lock the file for editing
await this.lockFile(minioClient, bucketName, fileUrl);
// Download the file
await minioClient.fGetObject(bucketName, fileUrl, localName);
// Read the CSV file
let csvData = await fs.readFile(localName, 'utf8');
let rows = csvData.split('\n').filter(row => row.trim() !== '');
// Check if first column is ID
const headers = rows.length > 0 ? rows[0].split(',') : [];
const hasIdColumn = headers.length > 0 && headers[0].toLowerCase() === 'id';
// If ID exists in args[0] and we have an ID column, try to find and update the row
let rowUpdated = false;
if (hasIdColumn && args[0]) {
for (let i = 1; i < rows.length; i++) {
const rowValues = rows[i].split(',');
if (rowValues[0] === args[0]) {
// Update existing row
rows[i] = args.join(',');
rowUpdated = true;
break;
}
}
}
// If no row was updated, add a new row
if (!rowUpdated) {
rows.push(args.join(','));
}
// Write back to the file
await fs.writeFile(localName, rows.join('\n'));
// Upload the updated file
await minioClient.fPutObject(bucketName, fileUrl, localName);
GBLogEx.info(min, `Successfully saved data to Minio storage: ${fileUrl}`);
} catch (error) {
GBLogEx.error(min, `Error saving to Minio storage: ${error.message}`);
throw error;
} finally {
// Ensure the file is unlocked
await this.unlockFile(minioClient, bucketName, fileUrl);
// Clean up the local file
try {
await fs.unlink(localName);
} catch (cleanupError) {
GBLogEx.info(min, `Could not clean up local file: ${cleanupError.message}`);
}
}
return;
}
// Original legacy mode handling
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const botId = min.instance.botId;
const packagePath = GBUtil.getGBAIPath(botId, 'gbdata');
@ -986,11 +859,13 @@ export class SystemKeywords {
} catch (e) {
if (e.cause === 404) {
// Creates the file.
const blank = path.join(process.env.PWD, 'blank.xlsx');
const data = await fs.readFile(blank);
await client.api(`${baseUrl}/drive/root:/${packagePath}/${file}:/content`).put(data);
// Tries to open again.
document = await this.internalGetDocument(client, baseUrl, packagePath, file);
sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
} else {
@ -1001,44 +876,51 @@ export class SystemKeywords {
let address;
let body = { values: [[]] };
// TODO: Check if first column is ID
// const firstCell = await client
// .api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A1')`)
// .get();
// Processes FILTER option to ensure parallel SET calls.
// const hasIdColumn = firstCell.text.toLowerCase() === 'id';
const filter = await DialogKeywords.getOption({ pid, name: 'filter' });
if (filter) {
// Creates id row.
// // If ID exists in args[0] and we have an ID column, try to find and update the row
// let rowUpdated = false;
// if (hasIdColumn && args[0]) {
// const allRows = await client
// .api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/usedRange`)
// .get();
body.values[0][0] = 'id';
const addressId = 'A1:A1';
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${addressId}')`
)
.patch(body);
body.values[0][0] = undefined;
// for (let i = 1; i < allRows.values.length; i++) {
// if (allRows.values[i][0] === args[0]) {
// // Update existing row
// address = `A${i + 1}:${this.numberToLetters(args.length - 1)}${i + 1}`;
// for (let j = 0; j < args.length; j++) {
// body.values[0][j] = args[j];
// }
// rowUpdated = true;
// break;
// }
// }
// }
const rowUpdated = false;
// If no row was updated, add a new row
if (!rowUpdated) {
// FINDs the filtered row to be updated.
const row = await this.find({ pid, handle: null, args: [file, filter] });
if (row) {
address = `A${row['line']}:${this.numberToLetters(args.length)}${row['line']}`;
}
}
// Editing or saving detection.
if (!address) {
await client
.api(
`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A2:DX2')/insert`
)
.post({});
address = `A2:${this.numberToLetters(args.length - 1)}2`;
for (let j = 0; j < args.length; j++) {
body.values[0][j] = args[j];
}
// Fills rows object to call sheet API.
for (let index = 0; index < args.length; index++) {
let value = args[index];
if (value && (await this.isValidDate({ pid, dt: value }))) {
value = `'${value}`;
}
// If filter is defined, skips id column.
body.values[0][filter ? index + 1 : index] = value;
}
await retry(
@ -1064,30 +946,6 @@ export class SystemKeywords {
);
}
// Helper methods for Minio file locking (unchanged)
private async lockFile(minioClient: Client, bucketName: string, filePath: string): Promise<void> {
const lockFile = `${filePath}.lock`;
try {
await minioClient.statObject(bucketName, lockFile);
throw new Error(`File ${filePath} is currently locked for editing`);
} catch (error) {
if (error.code === 'NotFound') {
// Create lock file
await minioClient.putObject(bucketName, lockFile, 'locked');
return;
}
throw error;
}
}
private async unlockFile(minioClient: Client, bucketName: string, filePath: string): Promise<void> {
const lockFile = `${filePath}.lock`;
try {
await minioClient.removeObject(bucketName, lockFile);
} catch (error) {
GBLog.error(`Error removing lock file: ${error.message}`);
}
}
/**
* Retrives the content of a cell in a tabular file.
*
@ -1270,7 +1128,7 @@ export class SystemKeywords {
const localName = path.join('work', gbaiName, 'cache', `csv${GBAdminService.getRndReadableIdentifier()}.csv`);
const url = file['@microsoft.graph.downloadUrl'];
const response = await fetch(url);
await fs.writeFile(localName, new Uint8Array(Buffer.from(await response.arrayBuffer())), { encoding: null });
await fs.writeFile(localName, Buffer.from(await response.arrayBuffer()), { encoding: null });
var workbook = new Excel.Workbook();
let worksheet = await workbook.csv.readFile(localName);
@ -1317,47 +1175,11 @@ export class SystemKeywords {
} else if (file.indexOf('.csv') !== -1) {
let res;
let packagePath = GBUtil.getGBAIPath(min.botId, `gbdata`);
let csvFile = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath, file);
if (GBConfigService.get('GB_MODE') === 'gbcluster') {
const fileUrl = urlJoin('/', `${min.botId}.gbdata`, file);
GBLogEx.info(min, `Direct data from .csv: ${fileUrl}.`);
const fileOnly = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost',
port: parseInt(process.env.DRIVE_PORT || '9000', 10),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET
});
const gbaiName = GBUtil.getGBAIPath(min.botId);
const bucketName = (process.env.DRIVE_ORG_PREFIX + min.botId + '.gbai').toLowerCase();
const localName = path.join(
process.env.PWD,
'work',
gbaiName,
'cache',
`${fileOnly.replace(/\s/gi, '')}-${GBAdminService.getNumberIdentifier()}.csv`
);
await minioClient.fGetObject(bucketName, fileUrl, localName);
csvFile = localName;
GBLogEx.info(min, `Downloaded .csv: ${csvFile}.`);
}
GBLogEx.info(min, `Reading .csv: ${csvFile}.`);
const csvFile = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath, file);
const data = await fs.readFile(csvFile, 'utf8');
const firstLine = data.split('\n')[0];
const headers = firstLine.split(',');
const db = await csvdb(csvFile, headers, ',');
GBLogEx.info(min, `READ .csv: ${csvFile}.`);
if (args[0]) {
const systemFilter = await SystemKeywords.getFilter(args[0]);
let filter = {};
@ -1367,8 +1189,6 @@ export class SystemKeywords {
res = await db.get();
}
GBLogEx.info(min, `ROWS: ${res.length}.`);
return res.length > 1 ? res : res[0];
} else {
const t = this.getTableFromName(file, min);
@ -1878,9 +1698,13 @@ export class SystemKeywords {
const srcPath = urlJoin(root, src);
const dstPath = urlJoin(packagePath, dest);
if (path.extname(srcPath) === 'ai') {
// TODO: To be done.
} else {
// Checks if the destination contains subfolders that
// need to be created.
@ -2161,98 +1985,35 @@ export class SystemKeywords {
*
* Fills a .docx or .pptx with template data.
*
* doc = FILL "templates/template.docx" WITH data
* doc = FILL "templates/template.docx", data
*
*/
private async getTemplateBuffer(min: any, gbaiName: string, templateName: string): Promise<Buffer> {
public async fill({ pid, templateName, data }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const botId = min.instance.botId;
const gbaiName = GBUtil.getGBAIPath(botId, 'gbdata');
let localName;
if (GBConfigService.get('GB_MODE') === 'legacy') {
// Legacy mode - using Microsoft Graph API
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const packagePath = '/' + urlJoin(gbaiName, `${botId}.gbdrive`);
const template = await this.internalGetDocument(client, baseUrl, packagePath, templateName);
const url = template['@microsoft.graph.downloadUrl'];
const res = await fetch(url);
return Buffer.from(await res.arrayBuffer());
} else if (GBConfigService.get('GB_MODE') === 'gbcluster') {
// GBCluster mode - using MinIO
const minioClient = this.createMinioClient();
const bucketName = (process.env.DRIVE_ORG_PREFIX + botId + '.gbai').toLowerCase();
const filePath = urlJoin(gbaiName, `${botId}.gbdrive`, templateName);
// Downloads template from .gbdrive.
return new Promise((resolve, reject) => {
const chunks: Uint8Array[] = [];
minioClient
.getObject(bucketName, filePath)
.then(stream => {
stream.on('data', chunk => chunks.push(new Uint8Array(chunk)));
stream.on('end', () => resolve(Buffer.concat(chunks)));
stream.on('error', reject);
})
.catch(reject);
});
} else {
// Default mode - direct filesystem access
const gbdriveName = GBUtil.getGBAIPath(botId, 'gbdrive');
const templatePath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbdriveName, templateName);
return fs.readFile(templatePath);
}
}
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
let packagePath = '/' + urlJoin(gbaiName, `${botId}.gbdrive`);
let template = await this.internalGetDocument(client, baseUrl, packagePath, templateName);
let url = template['@microsoft.graph.downloadUrl'];
const res = await fetch(url);
let buf: any = Buffer.from(await res.arrayBuffer());
localName = path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
await fs.writeFile(localName, buf, { encoding: null });
private async getImageBuffer(min: any, gbaiName: string, imagePath: string): Promise<Buffer> {
const botId = min.instance.botId;
// Replace image path on all elements of data.
if (GBConfigService.get('GB_MODE') === 'legacy') {
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
let packagePath = urlJoin(gbaiName, `${botId}.gbdrive`);
if (imagePath.indexOf('/') !== -1) {
packagePath = '/' + urlJoin(packagePath, path.dirname(imagePath));
imagePath = path.basename(imagePath);
}
const ref = await this.internalGetDocument(client, baseUrl, packagePath, imagePath);
const url = ref['@microsoft.graph.downloadUrl'];
const response = await fetch(url);
return Buffer.from(await response.arrayBuffer());
} else if (GBConfigService.get('GB_MODE') === 'gbcluster') {
const minioClient = this.createMinioClient();
const bucketName = (process.env.DRIVE_ORG_PREFIX + botId + '.gbai').toLowerCase();
const filePath = urlJoin(gbaiName, `${botId}.gbdrive`, imagePath);
return new Promise(async (resolve, reject) => {
const chunks: Buffer[] = [];
try {
const stream = await minioClient.getObject(bucketName, filePath);
stream.on('data', chunk => chunks.push(chunk));
stream.on('end', () => resolve(Buffer.concat(chunks)));
stream.on('error', reject);
} catch (err) {
reject(err);
}
});
} else {
const gbdriveName = GBUtil.getGBAIPath(botId, 'gbdrive');
const fullPath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbdriveName, imagePath);
return fs.readFile(fullPath);
}
}
private createMinioClient(): Client {
return new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost',
port: parseInt(process.env.DRIVE_PORT || '9000', 10),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET
});
}
private async processImagesInData(min: any, gbaiName: string, data: any): Promise<any[]> {
const images = [];
let index = 0;
packagePath = path.join(gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
const traverseDataToInjectImageUrl = async (o: any) => {
for (const i in o) {
const traverseDataToInjectImageUrl = async o => {
for (var i in o) {
let value = o[i];
if (value && value.gbarray) {
@ -2261,84 +2022,112 @@ export class SystemKeywords {
}
for (const kind of ['png', 'jpg', 'jpeg']) {
if (value?.endsWith?.(`.${kind}`)) {
const imageBuffer = await this.getImageBuffer(min, gbaiName, value);
if (value.endsWith && value.endsWith(`.${kind}`)) {
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
packagePath = urlJoin(gbaiName, `${botId}.gbdrive`);
if (value.indexOf('/') !== -1) {
packagePath = '/' + urlJoin(packagePath, path.dirname(value));
value = path.basename(value);
}
const ref = await this.internalGetDocument(client, baseUrl, packagePath, value);
let url = ref['@microsoft.graph.downloadUrl'];
const imageName = path.join(
'work',
gbaiName,
'cache',
`tmp${GBAdminService.getRndReadableIdentifier()}-${path.basename(value)}.png`
`tmp${GBAdminService.getRndReadableIdentifier()}-${value}.png`
);
await fs.writeFile(imageName, new Uint8Array(imageBuffer), { encoding: null });
const response = await fetch(url);
const buf = Buffer.from(await response.arrayBuffer());
await fs.writeFile(imageName, buf, { encoding: null });
const getNormalSize = ({ width, height, orientation }: any) => {
const getNormalSize = ({ width, height, orientation }) => {
return (orientation || 0) >= 5 ? [height, width] : [width, height];
};
// TODO: sharp. const metadata = await sharp(buf).metadata();
const size = getNormalSize({
width: 400,
height: 400,
orientation: '0'
});
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(imageName));
images[index++] = { url, size, buf: imageBuffer };
url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(imageName));
images[index++] = { url: url, size: size, buf: buf };
}
}
if (o[i] !== null && typeof o[i] === 'object') {
if (o[i] !== null && typeof o[i] == 'object') {
await traverseDataToInjectImageUrl(o[i]);
}
}
};
await traverseDataToInjectImageUrl(data);
return images;
}
public async fill({ pid, templateName, data }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const botId = min.instance.botId;
const gbaiName = GBUtil.getGBAIPath(botId);
// Get template buffer based on GB_MODE
const templateBuffer = await this.getTemplateBuffer(min, gbaiName, templateName);
// Process images in data
const images = await this.processImagesInData(min, gbaiName, data);
// Prepare local file
const localName = path.join('work', gbaiName, 'cache', `tmp${GBAdminService.getRndReadableIdentifier()}.docx`);
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
// Prepare docxtemplater options
let indexImage = 0;
const opts = {
var opts = {
fileType: 'docx',
centered: false,
getImage: () => images[indexImage].buf,
getSize: () => images[indexImage++].size
getImage: (tagValue, tagName) => {
return images[indexImage].buf;
},
getSize: (img, tagValue, tagName) => {
return images[indexImage++].size;
}
};
// Process the template
const zip = new PizZip(templateBuffer);
const doc = new Docxtemplater();
// Loads the file as binary content.
let zip = new PizZip(buf);
let doc = new Docxtemplater();
doc.setOptions({ paragraphLoop: true, linebreaks: true });
doc.loadZip(zip);
if (localName.endsWith('.pptx')) {
doc.attachModule(pptxTemplaterModule);
}
doc.attachModule(new ImageModule(opts));
doc.render(data);
const outputBuffer = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' });
await fs.writeFile(localName, new Uint8Array(outputBuffer), { encoding: null });
await traverseDataToInjectImageUrl(data);
doc.setData(data).render();
return { localName, url, data: outputBuffer };
buf = doc.getZip().generate({ type: 'nodebuffer', compression: 'DEFLATE' });
await fs.writeFile(localName, buf, { encoding: null });
return { localName: localName, url: url, data: buf };
}
public screenCapture(pid) {}
public screenCapture(pid) {
// scrcpy Disabled
// function captureImage({ x, y, w, h }) {
// const pic = robot.screen.capture(x, y, w, h)
// const width = pic.byteWidth / pic.bytesPerPixel // pic.width is sometimes wrong!
// const height = pic.height
// const image = new Jimp(width, height)
// let red, green, blue
// pic.image.forEach((byte, i) => {
// switch (i % 4) {
// case 0: return blue = byte
// case 1: return green = byte
// case 2: return red = byte
// case 3:
// image.bitmap.data[i - 3] = red
// image.bitmap.data[i - 2] = green
// image.bitmap.data[i - 1] = blue
// image.bitmap.data[i] = 255
// }
// })
// return image
// }
// let file = 'out.png';
// captureImage({ x: 60, y: 263, w: 250, h: 83 }).write(file)
// const config = {
// lang: "eng",
// oem: 1,
// psm: 3,
// }
// tesseract.recognize(file, config).then(value => {
// console.log(value);
// });
}
private numberToLetters(num) {
let letters = '';
@ -2689,11 +2478,12 @@ export class SystemKeywords {
GBLogEx.info(min, `BlueSky Automation: ${text}.`);
}
/**
*/
public async answer({ pid, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const answer = await ChatServices.answerByLLM(pid, min, user, text);
const answer = await ChatServices.answerByLLM(pid, min, user, text)
GBLogEx.info(min, `ANSWER ${text} TO ${answer}`);
return answer.answer;
}
@ -2705,9 +2495,9 @@ export class SystemKeywords {
*/
public async rewrite({ pid, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid);
const prompt = `${text}`;
const prompt = `Rewrite this sentence in a better way: ${text}`;
const answer = await ChatServices.invokeLLM(min, prompt);
GBLogEx.info(min, `REWRITE ${text} TO ${answer.text}`);
GBLogEx.info(min, `REWRITE ${text} TO ${answer}`);
return answer;
}
@ -2768,7 +2558,7 @@ export class SystemKeywords {
const buf = Buffer.from(data.Payment.QrCodeBase64Image, 'base64');
const localName = path.join('work', gbaiName, 'cache', `qr${GBAdminService.getRndReadableIdentifier()}.png`);
await fs.writeFile(localName, new Uint8Array(buf), { encoding: null });
await fs.writeFile(localName, buf, { encoding: null });
const url = urlJoin(GBServer.globals.publicAddress, min.botId, 'cache', path.basename(localName));
GBLogEx.info(min, `GBPay: ${data.MerchantOrderId} OK: ${url}.`);
@ -2857,6 +2647,7 @@ export class SystemKeywords {
}
public async getExtensionInfo(ext: any): Promise<any> {
// TODO: Load exts.
let array = []; // exts.filter((v, i, a) => a[i]['extension'] === ext);
@ -2935,7 +2726,7 @@ export class SystemKeywords {
try {
let data;
if (GBConfigService.get('GB_MODE') === 'legacy') {
if (GBConfigService.get('STORAGE_NAME')) {
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const gbaiName = GBUtil.getGBAIPath(min.botId);
let packagePath = '/' + urlJoin(gbaiName, `${min.botId}.gbdrive`);
@ -3000,7 +2791,7 @@ export class SystemKeywords {
// Criação de um arquivo temporário para enviar
const tempFilePath = path.resolve('temp_image.jpg');
await fs.writeFile(tempFilePath, new Uint8Array(imageBuffer));
await fs.writeFile(tempFilePath, imageBuffer);
// Publicação da imagem
const page = new Page(pageId);
@ -3117,16 +2908,17 @@ export class SystemKeywords {
}
public async convertAI2HTML(aiFilePath) {
// Convert the AI file to HTML and assets
const result = await ai2html.convertFile(aiFilePath, {
outputFormat: 'html',
outputWriteMethod: 'write-file',
outputPath: path.dirname(aiFilePath),
useDocumentSettings: true
useDocumentSettings: true,
});
// Get the generated HTML file path
const htmlFilePath = result.outputFiles.find(file => file.endsWith('.html')).filePath;
const htmlFilePath = result.outputFiles.find((file) => file.endsWith('.html')).filePath;
// Read the HTML content
const htmlContent = await fs.readFile(htmlFilePath, 'utf8');
@ -3138,8 +2930,10 @@ export class SystemKeywords {
await fs.writeFile(cacheFilePath, htmlContent);
return cacheFilePath;
}
public async refreshDataSourceCache({ pid, connectionName }) {
const { min, user, params, step } = await DialogKeywords.getProcessInfo(pid);
@ -3165,25 +2959,25 @@ export class SystemKeywords {
const tables = await GBUtil.listTables(dialect, con);
// Function to map source database datatypes to SQLite-compatible datatypes
const mapToSQLiteType = columnType => {
const mapToSQLiteType = (columnType) => {
const typeMapping = {
VARCHAR: DataTypes.STRING,
CHAR: DataTypes.STRING,
TEXT: DataTypes.TEXT,
TINYINT: DataTypes.INTEGER,
SMALLINT: DataTypes.INTEGER,
MEDIUMINT: DataTypes.INTEGER,
INT: DataTypes.INTEGER,
INTEGER: DataTypes.INTEGER,
BIGINT: DataTypes.BIGINT,
FLOAT: DataTypes.FLOAT,
DOUBLE: DataTypes.DOUBLE,
DECIMAL: DataTypes.DECIMAL,
DATE: DataTypes.DATE,
DATETIME: DataTypes.DATE,
TIMESTAMP: DataTypes.DATE,
BLOB: DataTypes.BLOB,
BOOLEAN: DataTypes.BOOLEAN
'VARCHAR': DataTypes.STRING,
'CHAR': DataTypes.STRING,
'TEXT': DataTypes.TEXT,
'TINYINT': DataTypes.INTEGER,
'SMALLINT': DataTypes.INTEGER,
'MEDIUMINT': DataTypes.INTEGER,
'INT': DataTypes.INTEGER,
'INTEGER': DataTypes.INTEGER,
'BIGINT': DataTypes.BIGINT,
'FLOAT': DataTypes.FLOAT,
'DOUBLE': DataTypes.DOUBLE,
'DECIMAL': DataTypes.DECIMAL,
'DATE': DataTypes.DATE,
'DATETIME': DataTypes.DATE,
'TIMESTAMP': DataTypes.DATE,
'BLOB': DataTypes.BLOB,
'BOOLEAN': DataTypes.BOOLEAN,
// Add more mappings as needed
};

View file

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

View file

@ -6,7 +6,7 @@
"title": "Default General Bot",
"description": "Default General Bot",
"whoAmIVideo": "TODO.mp4",
"author": "pragmatismo.com.br",
"author": "pragmatismo.cloud",
"license": "AGPL",
"engineName": "guaribas-1.0.0"
}

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -65,7 +65,8 @@ export class WelcomeDialog extends IGBDialog {
async step => {
if (
GBServer.globals.entryPointDialog !== null &&
min.instance.botId === GBConfigService.get('BOT_ID')
min.instance.botId === GBConfigService.get('BOT_ID') &&
step.context.activity.channelId === 'webchat'
) {
return step.replaceDialog(GBServer.globals.entryPointDialog);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -86,7 +86,7 @@ export class GBConfigService {
value = this.getServerPort();
break;
case 'GBVM':
value = true;
value = false;
break;
case 'STORAGE_NAME':
value = null;

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -43,7 +43,6 @@ import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { SecService } from '../../security.gbapp/services/SecService.js';
import { AnalyticsService } from '../../analytics.gblib/services/AnalyticsService.js';
import { MicrosoftAppCredentials } from 'botframework-connector';
import { DocxLoader } from '@langchain/community/document_loaders/fs/docx';
import { GBConfigService } from './GBConfigService.js';
import { CollectionUtil, AzureText } from 'pragmatismo-io-framework';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
@ -53,7 +52,7 @@ import { createWriteStream, createReadStream } from 'fs';
import fs from 'fs/promises';
import twilio from 'twilio';
import Nexmo from 'nexmo';
import path, { join } from 'path';
import { join } from 'path';
import shell from 'any-shell-escape';
import { exec } from 'child_process';
import prism from 'prism-media';
@ -474,7 +473,7 @@ export class GBConversationalService {
return new Promise<string>(async (resolve, reject) => {
try {
const oggFile = new Readable();
oggFile._read = () => { }; // _read is required but you can noop it
oggFile._read = () => {}; // _read is required but you can noop it
oggFile.push(buffer);
oggFile.push(null);
@ -647,15 +646,6 @@ export class GBConversationalService {
text.toLowerCase().endsWith('.png') ||
text.toLowerCase().endsWith('.mp4') ||
text.toLowerCase().endsWith('.mov');
if (text.endsWith('-zap')) {
let packagePath = GBUtil.getGBAIPath(min.botId, `gbkb`);
const localName = path.join('work', packagePath, 'articles', text + '.docx');
let loader = new DocxLoader(localName);
let doc = await loader.load();
text = doc[0].pageContent;
template = template + '_docx';
}
let mediaFile = !isMedia ? /(.*)\n/gim.exec(text)[0].trim() : text;
let mediaType = mediaFile.toLowerCase().endsWith('.mp4') || text.toLowerCase().endsWith('.mov') ? 'video' : 'image';
@ -1211,8 +1201,8 @@ export class GBConversationalService {
public async sendTextWithOptions(min: GBMinInstance, step, text, translate, keepTextList, user) {
let sec = new SecService();
if (!user) {
if (!user){
user = await sec.getUserFromSystemId(step.context.activity.from.id);
}
await this['sendTextWithOptionsAndUser'](min, user, step, text, true, null);
@ -1262,11 +1252,11 @@ export class GBConversationalService {
analytics.createMessage(min.instance.instanceId, conversation, null, text);
}
if (!isNaN(user.userSystemId)) {
if (!isNaN(user.userSystemId)){
await min.whatsAppDirectLine.sendToDevice(user.userSystemId, text);
await min.whatsAppDirectLine.sendToDevice(user.userSystemId, text);
}
else {
else{
await step.context.sendActivity(text);
}
@ -1290,24 +1280,24 @@ export class GBConversationalService {
* Sends a message in a user with an already started conversation (got ConversationReference set)
*/
public async sendOnConversation(min: GBMinInstance, user: GuaribasUser, message: any) {
if (GBConfigService.get('GB_MODE') === 'legacy') {
const ref = JSON.parse(user.conversationReference);
MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl);
await min.bot['continueConversation'](ref, async t1 => {
const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async t2 => {
await t2.sendActivity(message);
if (GBConfigService.get('STORAGE_NAME')) {
const ref = JSON.parse(user.conversationReference);
MicrosoftAppCredentials.trustServiceUrl(ref.serviceUrl);
await min.bot['continueConversation'](ref, async t1 => {
const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async t2 => {
await t2.sendActivity(message);
});
});
});
} else {
const ref = JSON.parse(user.conversationReference);
await min.bot['continueConversation'](ref, async (t1) => {
const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async (t2) => {
await t2.sendActivity(message);
});
const ref2 = TurnContext.getConversationReference(t1.activity);
await min.bot.continueConversation(ref2, async (t2) => {
await t2.sendActivity(message);
});
});
if (message['buttons'] || message['sections']) {

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -67,7 +67,6 @@ 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
@ -113,7 +112,7 @@ export class GBCoreService implements IGBCoreService {
constructor() {
this.adminService = new GBAdminService(this);
}
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) { }
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {}
/**
* Gets database config and connect to storage. Currently two databases
@ -122,86 +121,62 @@ export class GBCoreService implements IGBCoreService {
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') {
if (this.dialect === 'mssql') {
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';
}
} else {
throw new Error(`Unknown dialect: ${this.dialect}.`);
}
const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => {
GBLogEx.info(0, str);
}
GBLogEx.info(0, str);
}
: false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
const acquireStr = GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT');
const acquire = acquireStr ? parseInt(acquireStr, 10) : 10000; // Valor padrão de 10 segundos
const acquire = parseInt(GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT'));
const sequelizeOptions: SequelizeOptions = {
define: {
freezeTableName: true,
timestamps: false,
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' ? {
quoteIdentifiers: false, // set case-insensitive
dialectOptions: {
options: {
trustServerCertificate: true,
encrypt: encrypt,
},
} : {},
encrypt: encrypt
}
},
pool: {
max: 5,
min: 0,
idle: 10000,
evict: 10000,
acquire: acquire,
},
acquire: acquire
}
};
this.sequelize = new Sequelize(database, username, password, sequelizeOptions);
}
@ -277,15 +252,12 @@ export class GBCoreService implements IGBCoreService {
const options = {
where: {
[Op.or]: and
},
order: [['instanceId', 'ASC']]
}
};
return await GuaribasInstance.findAll(options as any);
return await GuaribasInstance.findAll(options);
} else {
const options = { where: { state: 'active' } ,
order: [['instanceId', 'ASC']]};
return await GuaribasInstance.findAll(options as any);
const options = { where: { state: 'active' } };
return await GuaribasInstance.findAll(options);
}
}
@ -323,6 +295,7 @@ export class GBCoreService implements IGBCoreService {
public async writeEnv(instance: IGBInstance) {
const env = `
ADDITIONAL_DEPLOY_PATH=
ADMIN_PASS=${instance.adminPass}
BOT_ID=${instance.botId}
CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}
CLOUD_LOCATION=${instance.cloudLocation}
@ -340,7 +313,7 @@ STORAGE_SYNC_ALTER=true
ENDPOINT_UPDATE=true
`;
await fs.writeFile('.env', env);
await fs.writeFile('.env', env);
}
/**
@ -550,7 +523,7 @@ ENDPOINT_UPDATE=true
* Verifies that an complex global password has been specified
* before starting the server.
*/
public ensureAdminIsSecured() { }
public ensureAdminIsSecured() {}
public async createBootInstance(
core: GBCoreService,
@ -702,30 +675,30 @@ ENDPOINT_UPDATE=true
}
public async setConfig(min, name: string, value: any): Promise<any> {
if (GBConfigService.get('GB_MODE') === 'legacy') {
if (GBConfigService.get('STORAGE_NAME')) {
// 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];
@ -734,27 +707,27 @@ ENDPOINT_UPDATE=true
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
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`);
} else {
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 });
@ -763,7 +736,7 @@ ENDPOINT_UPDATE=true
}
}
}
/**
* Get a dynamic param from instance. Dynamic params are defined in Config.xlsx
* and loaded into the work folder from comida command.
@ -863,72 +836,75 @@ ENDPOINT_UPDATE=true
}
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 CollectionUtil.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);
}
});
if (!(await GBUtil.exists(libraryPath))) {
mkdirp.sync(libraryPath);
}
await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const files = await fs.readdir(libraryPath);
await CollectionUtil.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...`);
if (!instance) {
GBLog.info(`Importing package ${botId}...`);
// Creates a bot.
// Creates a bot.
let mobile = null,
email = null;
let mobile = null,
email = null;
instance = await deployer.deployBlankBot(botId, mobile, email);
instances.push(instance);
const gbaiPath = path.join(libraryPath, `${botId}.gbai`);
instance = await deployer.deployBlankBot(botId, mobile, email);
const gbaiPath = path.join(libraryPath, `${botId}.gbai`);
if (!(await GBUtil.exists(gbaiPath))) {
fs.mkdir(gbaiPath, { recursive: true });
}
if (!(await GBUtil.exists(gbaiPath))) {
fs.mkdir(gbaiPath, { recursive: true });
const base = path.join(process.env.PWD, 'templates', 'default.gbai');
fs.cp(path.join(base, `default.gbkb`), path.join(gbaiPath, `default.gbkb`), {
errorOnExist: false,
force: true,
recursive: true
});
fs.cp(path.join(base, `default.gbot`), path.join(gbaiPath, `default.gbot`), {
errorOnExist: false,
force: true,
recursive: true
});
fs.cp(path.join(base, `default.gbtheme`), path.join(gbaiPath, `default.gbtheme`), {
errorOnExist: false,
force: true,
recursive: true
});
fs.cp(path.join(base, `default.gbdata`), path.join(gbaiPath, `default.gbdata`), {
errorOnExist: false,
force: true,
recursive: true
});
fs.cp(path.join(base, `default.gbdialog`), path.join(gbaiPath, `default.gbdialog`), {
errorOnExist: false,
force: true,
recursive: true
});
fs.cp(path.join(base, `default.gbdrive`), path.join(gbaiPath, `default.gbdrive`), {
errorOnExist: false,
force: true,
recursive: true
});
}
}
return instance;
}
public static async createWebDavServer(minInstances: GBMinInstance[]) {

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -39,8 +39,6 @@ import express from 'express';
import child_process from 'child_process';
import { rimraf } from 'rimraf';
import urlJoin from 'url-join';
import { Client } from 'minio';
import fs from 'fs/promises';
import { GBError, GBLog, GBMinInstance, IGBCoreService, IGBDeployer, IGBInstance, IGBPackage } from 'botlib';
import { AzureSearch } from 'pragmatismo-io-framework';
@ -49,7 +47,7 @@ import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import Excel from 'exceljs';
import asyncPromise from 'async-promises';
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel.js';
import { GuaribasPackage } from '../models/GBModel.js';
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { KBService } from './../../kb.gbapp/services/KBService.js';
@ -222,7 +220,7 @@ export class GBDeployer implements IGBDeployer {
const instance = await this.importer.createBotInstance(botId);
const bootInstance = GBServer.globals.bootInstance;
if (GBConfigService.get('GB_MODE') === 'legacy') {
if (GBConfigService.get('STORAGE_NAME')) {
// Gets the access token to perform service operations.
const accessToken = await (GBServer.globals.minBoot.adminService as any)['acquireElevatedToken'](
@ -234,14 +232,13 @@ export class GBDeployer implements IGBDeployer {
const service = await AzureDeployerService.createInstance(this);
const application = await service.createApplication(accessToken, botId);
// Fills new instance base information and get App secret.
instance.marketplaceId = (application as any).appId;
instance.marketplacePassword = await service.createApplicationSecret(accessToken, (application as any).id);
}
instance.adminPass = await GBUtil.hashPassword(GBAdminService.getRndPassword());
instance.adminPass = GBAdminService.getRndPassword();
instance.title = botId;
instance.activationCode = instance.botId.substring(0, 15);
instance.state = 'active';
@ -252,14 +249,13 @@ export class GBDeployer implements IGBDeployer {
// Saves bot information to the store.
await this.core.saveInstance(instance);
if (GBConfigService.get('GB_MODE') === 'legacy') {
if (GBConfigService.get('STORAGE_NAME')) {
await this.deployBotOnAzure(instance, GBServer.globals.publicAddress);
}
// Makes available bot to the channels and .gbui interfaces.
const min = await GBServer.globals.minService.mountBot(instance);
GBServer.globals.minInstances.push(min);
await GBServer.globals.minService.mountBot(instance);
// Creates remaining objects on the cloud and updates instance information.
@ -270,22 +266,9 @@ export class GBDeployer implements IGBDeployer {
* Verifies if bot exists on bot catalog.
*/
public async botExists(botId: string): Promise<boolean> {
const service = await AzureDeployerService.createInstance(this);
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const where = { botId: botId };
return await GuaribasInstance.findOne({
where: where
}) !== null;
}
else {
const service = await AzureDeployerService.createInstance(this);
return await service.botExists(botId);
}
return await service.botExists(botId);
}
/**
@ -371,20 +354,9 @@ export class GBDeployer implements IGBDeployer {
vectorStore = await HNSWLib.load(min['vectorStorePath'], embedding);
} catch (e) {
GBLogEx.info(min, `Creating new store...`);
vectorStore = await HNSWLib.fromTexts(
['This is General Bots.'], // Initial texts (empty)
{}, // Optional metadata
embedding,
{
'space': 'cosine',
} as any
);
const dir = path.dirname(min['vectorStorePath']);
if (!(await GBUtil.exists(dir))) {
fs.mkdir(dir, { recursive: true });
}
await vectorStore.save(min['vectorStorePath']);
vectorStore = new HNSWLib(embedding, {
space: 'cosine'
});
}
return vectorStore;
}
@ -494,13 +466,13 @@ export class GBDeployer implements IGBDeployer {
} else {
return [];
}
1
await asyncPromise.eachSeries(rows, async (line: any) => {
if (line && line.length > 0) {
const key = line[1];
let value = line[2];
if (key && value) {
if (value.text) { value = value.text };
obj[key] = value;
@ -515,135 +487,93 @@ export class GBDeployer implements IGBDeployer {
/**
*/
public async downloadFolder(
min: GBMinInstance,
localPath: string,
remotePath: string,
baseUrl: string = null,
client = null, onlyTextFiles = false
client = null
): Promise<any> {
const storageMode = process.env.GB_MODE;
GBLogEx.info(min, `downloadFolder: localPath=${localPath}, remotePath=${remotePath}, baseUrl=${baseUrl}`);
if (storageMode === 'gbcluster') {
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost',
port: parseInt(process.env.DRIVE_PORT || '9000', 10),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET,
});
if (!baseUrl) {
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const bucketName = (process.env.DRIVE_ORG_PREFIX + min.botId + '.gbai').toLowerCase();
remotePath = remotePath.replace(/\\/gi, '/');
const parts = remotePath.split('/');
if (!(await GBUtil.exists(localPath))) {
await fs.mkdir(localPath, { recursive: true });
// Creates each subfolder.
let pathBase = localPath;
if (!(await GBUtil.exists(pathBase))) {
fs.mkdir(pathBase);
}
const objectsStream = minioClient.listObjects(bucketName, remotePath, true);
for await (const obj of objectsStream) {
const itemPath = path.join(localPath, obj.name);
await CollectionUtil.asyncForEach(parts, async item => {
pathBase = path.join(pathBase, item);
if (!(await GBUtil.exists(pathBase))) {
fs.mkdir(pathBase);
}
});
if (obj.name.endsWith('/')) {
// Retrieves all files in remote folder.
let packagePath = GBUtil.getGBAIPath(min.botId);
packagePath = urlJoin(packagePath, remotePath);
let url = `${baseUrl}/drive/root:/${packagePath}:/children`;
GBLogEx.info(min, `Downloading: ${url}`);
let documents;
try {
const res = await client.api(url).get();
documents = res.value;
} catch (error) {
GBLogEx.info(min, `Error downloading: ${error.toString()}`);
}
if (documents === undefined || documents.length === 0) {
GBLogEx.info(min, `${remotePath} is an empty folder.`);
return null;
}
// Download files or navigate to directory to recurse.
await CollectionUtil.asyncForEach(documents, async item => {
const itemPath = path.join(localPath, remotePath, item.name);
if (item.folder) {
if (!(await GBUtil.exists(itemPath))) {
await fs.mkdir(itemPath, { recursive: true });
fs.mkdir(itemPath);
}
const nextFolder = urlJoin(remotePath, item.name);
await this.downloadFolder(min, localPath, nextFolder);
} else {
let download = true;
if (await GBUtil.exists(itemPath)) {
const stats = await fs.stat(itemPath);
if (stats.mtime >= new Date(obj.lastModified)) {
const dt = await fs.stat(itemPath);
if (new Date(dt.mtime) >= new Date(item.lastModifiedDateTime)) {
download = false;
}
}
// Only download text files if onlyTextFiles flag is set
if (onlyTextFiles) {
// Check if file is NOT one of the allowed text file types
if (!obj.name.match(/\.(txt|json|csv|xlsx?|xlsm|xlsb|xml|html?|md|docx?|pdf|pptx?)$/i)) {
download = false;
}
}
if (download) {
await minioClient.fGetObject(bucketName, obj.name, itemPath);
await fs.utimes(itemPath, new Date(), new Date(obj.lastModified));
}
}
}
} else {
if (!baseUrl) {
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
GBLogEx.info(min, `Downloading: ${itemPath}...`);
const url = item['@microsoft.graph.downloadUrl'];
remotePath = remotePath.replace(/\\/gi, '/');
const parts = remotePath.split('/');
let pathBase = localPath;
if (!(await GBUtil.exists(pathBase))) {
await fs.mkdir(pathBase, { recursive: true });
}
await CollectionUtil.asyncForEach(parts, async (item) => {
pathBase = path.join(pathBase, item);
if (!(await GBUtil.exists(pathBase))) {
await fs.mkdir(pathBase, { recursive: true });
}
});
let packagePath = GBUtil.getGBAIPath(min.botId);
packagePath = urlJoin(packagePath, remotePath);
let url = `${baseUrl}/drive/root:/${packagePath}:/children`;
let documents;
try {
const res = await client.api(url).get();
documents = res.value;
} catch (error) {
GBLogEx.info(min, `Error downloading: ${error.toString()}`);
}
if (documents === undefined || documents.length === 0) {
return null;
}
await CollectionUtil.asyncForEach(documents, async (item) => {
const itemPath = path.join(localPath, remotePath, item.name);
if (item.folder) {
if (!(await GBUtil.exists(itemPath))) {
await fs.mkdir(itemPath, { recursive: true });
}
const nextFolder = urlJoin(remotePath, item.name);
await this.downloadFolder(min, localPath, nextFolder);
const response = await fetch(url);
await fs.writeFile(itemPath, Buffer.from(await response.arrayBuffer()), { encoding: null });
fs.utimes(itemPath, new Date(), new Date(item.lastModifiedDateTime));
} else {
let download = true;
if (await GBUtil.exists(itemPath)) {
const stats = await fs.stat(itemPath);
if (new Date(stats.mtime) >= new Date(item.lastModifiedDateTime)) {
download = false;
}
}
if (download) {
const url = item['@microsoft.graph.downloadUrl'];
const response = await fetch(url);
await fs.writeFile(itemPath, new Uint8Array(await response.arrayBuffer()), { encoding: null });
await fs.utimes(itemPath, new Date(), new Date(item.lastModifiedDateTime));
}
GBLogEx.info(min, `Local is up to date: ${path.basename(itemPath)}...`);
}
});
}
}
});
}
}
/**
* Undeploys a bot to the storage.
* UndDeploys a bot to the storage.
*/
public async undeployBot(botId: string, packageName: string): Promise<void> {
// Deletes Bot registration on cloud.
@ -691,21 +621,11 @@ export class GBDeployer implements IGBDeployer {
await this.cleanupPackage(min.instance, packageName);
}
if (GBConfigService.get('GB_MODE') === 'local') {
if (!GBConfigService.get('STORAGE_NAME')) {
const filePath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbai, packageName);
if (packageType === '.gbdrive' || packageType === '.gbdata') {
await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder, true);
} else {
await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder, false);
}
await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder);
} else {
if (packageType === '.gbdrive' || packageType === '.gbdata') {
await this.downloadFolder(min, path.join('work', `${gbai}`), packageName, undefined, undefined, true);
}
else {
await this.downloadFolder(min, path.join('work', `${gbai}`), packageName);
}
await this.downloadFolder(min, path.join('work', `${gbai}`), packageName);
}
}
@ -741,10 +661,6 @@ export class GBDeployer implements IGBDeployer {
// Deploy platform packages here accordingly to their extension.
switch (packageType) {
case '.gbdrive':
break;
case '.gbdata':
break;
case '.gbot':
// Extracts configuration information from .gbot files.
@ -762,7 +678,7 @@ export class GBDeployer implements IGBDeployer {
con['storageDriver'] = min.core.getParam<string>(min.instance, `${connectionName} Driver`, null);
con['storageTables'] = min.core.getParam<string>(min.instance, `${connectionName} Tables`, null);
const storageName = min.core.getParam<string>(min.instance, `${connectionName} Name`, null);
let file = min.core.getParam<string>(min.instance, `${connectionName} File`, null);
if (storageName) {

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -78,7 +78,7 @@ export class GBImporter {
if (!instance) {
instance = <IGBInstance>{};
instance.state = 'active';
instance.adminPass = await GBUtil.hashPassword( GBConfigService.get('ADMIN_PASS'));
instance.adminPass = GBConfigService.get('ADMIN_PASS');
instance.botId = GBConfigService.get('BOT_ID');
instance.cloudSubscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
instance.cloudLocation = GBConfigService.get('CLOUD_LOCATION');

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -37,7 +37,6 @@ import { createRpcServer } from '@push-rpc/core';
import AuthenticationContext from 'adal-node';
import arrayBufferToBuffer from 'arraybuffer-to-buffer';
import { Semaphore } from 'async-mutex';
import { AccessToken } from 'livekit-server-sdk';
import { Mutex } from 'async-mutex';
import chokidar from 'chokidar';
import cors from 'cors';
@ -106,7 +105,6 @@ import { GBConversationalService } from './GBConversationalService.js';
import { GBDeployer } from './GBDeployer.js';
import { GBLogEx } from './GBLogEx.js';
import { GBSSR } from './GBSSR.js';
import Stripe from 'stripe';
/**
* Minimal service layer for a bot and encapsulation of BOT Framework calls.
@ -162,15 +160,16 @@ export class GBMinService {
// Servers default UI on root address '/' if web enabled.
if (process.env.DISABLE_WEB !== 'true' || process.env.ENABLE_INSTANCE_ON_URL) {
// Servers the bot information object via HTTP so clients can get
// instance information stored on server.
GBServer.globals.server.use(
cors({
origin: 'https://gb6.pragmatismo.com.br',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'x-requested-with', 'x-ms-bot-agent']
})
);
GBServer.globals.server.use(cors({
origin: 'http://localhost:8081',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'x-requested-with'
, 'x-ms-bot-agent'
]
}));
GBServer.globals.server.get('/instances/:botId', this.handleGetInstanceForClient.bind(this));
}
@ -212,11 +211,14 @@ export class GBMinService {
const user = await sec.ensureUser(min, 'testuser', 'testuser', '', 'test', 'testuser', null);
const pid = GBVMService.createProcessInfo(user, min, 'api', null);
const response = await client.apis.Conversations.Conversations_StartConversation({
userSystemId: user.userSystemId,
userName: user.userName,
pid: pid
});
const response = await client.apis.Conversations.Conversations_StartConversation(
{
userSystemId: user.userSystemId,
userName: user.userName,
pid: pid
}
);
const conversationId = response.obj.conversationId;
GBServer.globals.debugConversationId = conversationId;
@ -235,7 +237,7 @@ export class GBMinService {
name: 'test',
channelIdEx: 'web',
pid: pid
}
},
}
});
@ -282,7 +284,7 @@ export class GBMinService {
/**
* Unmounts the bot web site (default.gbui) secure domain, if any.
*/
public async unloadDomain(instance: IGBInstance) {}
public async unloadDomain(instance: IGBInstance) { }
/**
* Mount the instance by creating an BOT Framework bot object,
@ -422,7 +424,7 @@ export class GBMinService {
const packageTeams = urlJoin(`work`, GBUtil.getGBAIPath(instance.botId), manifest);
if (!(await GBUtil.exists(packageTeams))) {
const data = await this.deployer.getBotManifest(instance);
//await fs.writeFile(packageTeams, data);
await fs.writeFile(packageTeams, data);
}
// Serves individual URL for each bot user interface.
@ -457,78 +459,26 @@ export class GBMinService {
this.createCheckHealthAddress(GBServer.globals.server, min, min.instance);
GBServer.globals.server
.all(`/${min.instance.botId}/paymentSuccess`, async (req, res) => {
try {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
GBLogEx.info(min, `Payment success webhook received for bot ${min.instance.botId}`);
const sessionId = req.query.session_id;
if (!sessionId) {
GBLogEx.info(min, 'No session_id parameter found in payment success callback');
return res.status(400).json({ success: false, error: 'Missing session_id parameter' });
}
const session = await stripe.checkout.sessions.retrieve(sessionId);
if (session.payment_status === 'paid') {
GBLogEx.info(min, `Payment confirmed for session ${sessionId}`);
// Only for successful payment - send HTML to close window
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Payment Successful</title>
<script>
// Close the window after a short delay
setTimeout(() => {
window.close();
}, 1000);
</script>
</head>
<body style="text-align: center; padding: 40px; font-family: Arial;">
<h1 style="color: #4CAF50;">Payment Successful!</h1>
<p>General Bots: Your transaction was completed successfully.</p>
</body>
</html>
`);
} else {
GBLogEx.info(min, `Payment not completed for session ${sessionId}`);
res.status(402).json({
success: false,
error: 'Payment not completed',
sessionId: sessionId
});
}
} catch (error) {
GBLogEx.error(min, `Error processing payment success: ${error.message}`);
res.status(500).json({
success: false,
error: error.message
});
}
})
.bind(min);
// Setups official handler for WhatsApp.
GBServer.globals.server
.all(`/${min.instance.botId}/whatsapp`, async (req, res) => {
const challenge = (min.core['getParam'] as any)(min.instance, `Meta Challenge`, null, true);
const status = req.body?.entry?.[0]?.changes?.[0]?.value?.statuses?.[0];
if (status) {
GBLogEx.info(min, `WhatsApp: ${status.recipient_id} ${status.status}`);
GBLogEx.verbose(min, `WhatsApp: ${status.recipient_id} ${status.status}`);
return;
}
if (req.query['hub.mode'] === 'subscribe') {
const val = req.query['hub.verify_token'];
const challenge = (min.core['getParam'] as any)(min.instance, `Meta Challenge`, null, true);
if (challenge && val === challenge) {
res.send(req.query['hub.challenge']);
res.status(200);
GBLogEx.info(min, `Meta callback OK. ${JSON.stringify(req.query)}`);
} else {
res.status(401);
}
@ -542,13 +492,8 @@ export class GBMinService {
// Not meta, multiples bots on root bot.
if (!req.body.object) {
if (req.body.To) {
const to = req.body.To.replace(/whatsapp\:\+/gi, '');
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
} else {
const minBoot = GBServer.globals.minBoot as GBMinInstance;
whatsAppDirectLine = minBoot.whatsAppDirectLine;
}
const to = req.body.To.replace(/whatsapp\:\+/gi, '');
whatsAppDirectLine = WhatsappDirectLine.botsByNumber[to];
}
if (whatsAppDirectLine) {
@ -557,50 +502,6 @@ export class GBMinService {
})
.bind(min);
GBServer.globals.server.all(`/${min.instance.botId}/meeting-token`, async (req, res) => {
try {
// Add to your route handler
res.setHeader('Access-Control-Allow-Origin', '*');
// 1. Validate request
const { room, identity, name } = req.query;
GBLog.info(`Meeting token asked. ${room} ${identity} ${name}`);
if (!room || !identity) {
return res.status(400).json({ error: 'Missing required parameters: room, identity' });
}
// 2. Get API keys from config (replace with your actual config access)
const apiKey = process.env.LIVEKIT_API_KEY;
const apiSecret = process.env.LIVEKIT_API_SECRET;
if (!apiKey || !apiSecret) {
return res.status(500).json({ error: 'Server misconfigured' });
}
// 3. Generate token
const token = new AccessToken(apiKey, apiSecret, {
identity: identity.toString(),
name: name?.toString() || identity.toString()
});
// 4. Set permissions
token.addGrant({
roomJoin: true,
room: room.toString(),
canPublish: true,
canSubscribe: true,
canPublishData: true
});
// 6. Return JWT
const jwt = await token.toJwt();
res.json({ token: jwt });
} catch (err) {
GBLog.error(`Meeting token generation failed: ${err}`);
res.status(500).json({ error: 'Token generation failed' });
}
});
GBDeployer.mountGBKBAssets(`${botId}.gbkb`, botId, `${botId}.gbkb`);
return min;
@ -628,14 +529,26 @@ export class GBMinService {
private createCheckHealthAddress(server: any, min: GBMinInstance, instance: IGBInstance) {
server.get(`/${min.instance.botId}/check`, async (req, res) => {
try {
// Performs the checking of WhatsApp API if enabled for this instance.
if (min.whatsAppDirectLine != undefined && instance.whatsappServiceKey !== null) {
if (!(await min.whatsAppDirectLine.check(min))) {
const error = `WhatsApp API lost connection for: ${min.botId}.`;
GBLog.error(error);
res.status(500).send(error);
return;
}
}
// GB is OK, so 200.
res.status(200).send(`General Bot ${min.botId} is healthly.`);
} catch (error) {
// GB is not OK, 500 and detail the information on response content.
GBLogEx.error(min, error);
res.status(500).send('Service with erros. Please, check service log.');
GBLog.error(error);
res.status(500).send(error.toString());
}
});
}
@ -769,9 +682,8 @@ export class GBMinService {
min.instance.authenticatorTenant,
'/oauth2/authorize'
);
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${
min.instance.marketplaceId
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
authorizationUrl = `${authorizationUrl}?response_type=code&client_id=${min.instance.marketplaceId
}&redirect_uri=${urlJoin(process.env.BOT_URL, min.instance.botId, 'token')}`;
GBLogEx.info(min, `HandleOAuthRequests: ${authorizationUrl}.`);
res.redirect(authorizationUrl);
});
@ -809,7 +721,7 @@ export class GBMinService {
let logo = this.core.getParam(instance, 'Logo', null);
logo = logo ? urlJoin(instance.botId, 'cache', logo) : 'https://pragmatismo.com.br/icons/general-bots-text.svg';
logo = logo ? urlJoin(instance.botId, 'cache', logo) : 'images/logo-gb.png';
let config = {
instanceId: instance.instanceId,
@ -828,12 +740,8 @@ export class GBMinService {
color2: this.core.getParam(instance, 'Color2', null)
};
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const url =
process.env.BOT_URL && !process.env.BOT_URL.includes('ngrok')
? process.env.BOT_URL
: `http://localhost:${GBConfigService.get('PORT')}`;
config['domain'] = urlJoin(url, 'directline', botId);
if (!GBConfigService.get('STORAGE_NAME')) {
config['domain'] = `http://localhost:${GBConfigService.get('PORT')}/directline/${botId}`;
} else {
const webchatTokenContainer = await this.getWebchatToken(instance);
config['conversationId'] = webchatTokenContainer.conversationId;
@ -904,11 +812,9 @@ export class GBMinService {
? instance.marketplacePassword
: GBConfigService.get('MARKETPLACE_SECRET')
};
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const url = process.env.BOT_URL || `http://localhost:${GBConfigService.get('PORT')}`;
if (!GBConfigService.get('STORAGE_NAME')) {
startRouter(GBServer.globals.server, instance.botId);
config['clientOptions'] = { baseUri: url };
config['clientOptions'] = { baseUri: `http://localhost:${GBConfigService.get('PORT')}` };
}
const adapter = new BotFrameworkAdapter(config);
@ -923,8 +829,8 @@ export class GBMinService {
// The minimal bot is built here.
const min = new GBMinInstance();
const min = new GBMinInstance();
// Setups default BOT Framework dialogs.
min.userProfile = conversationState.createProperty('userProfile');
@ -960,7 +866,8 @@ export class GBMinService {
min.sandBoxMap = {};
min['scheduleMap'] = {};
min['conversationWelcomed'] = {};
if ((await min.core.getParam(min.instance, 'Answer Mode', null)) && !min['vectorStore']) {
if (await min.core.getParam(min.instance, 'Answer Mode', null) &&
!min['vectorStore']) {
const gbkbPath = GBUtil.getGBAIPath(min.botId, 'gbkb');
min['vectorStorePath'] = path.join('work', gbkbPath, 'docs-vectorized');
min['vectorStore'] = await this.deployer.loadOrCreateEmptyVectorStore(min);
@ -1034,7 +941,7 @@ export class GBMinService {
await min.whatsAppDirectLine.setup(true);
} else {
if (min !== minBoot && minBoot.instance.whatsappServiceKey) {
if (min !== minBoot && minBoot.instance.whatsappServiceKey && min.instance.webchatKey) {
min.whatsAppDirectLine = new WhatsappDirectLine(
min,
min.botId,
@ -1133,6 +1040,7 @@ export class GBMinService {
// Default activity processing and handler.
const handler = async context => {
// Handle activity text issues.
if (!context.activity.text) {
@ -1144,58 +1052,21 @@ export class GBMinService {
const step = await min.dialogs.createContext(context);
step.context.activity.locale = 'pt-BR';
const sec = new SecService();
let member = context.activity.recipient;
if (context.activity.type === 'conversationUpdate') {
if (
context.activity.membersAdded &&
context.activity.membersAdded.length > 0 &&
context.activity.membersAdded[0].id === context.activity.recipient.id
) {
GBLogEx.info(min, `Bot added to conversation: ${member.name}`);
return;
}
}
if (process.env.GB_MODE === 'legacy' || !member) {
if (process.env.STORAGE_NAME || !member) {
member = context.activity.from;
}
let user = await sec.ensureUser(min, member.id, member.name, '', 'web', member.name, null);
const userId = user.userId;
const params = user.params ? JSON.parse(user.params) : {};
const t = new SystemKeywords();
try {
const conversationReference = JSON.stringify(TurnContext.getConversationReference(context.activity));
user = await sec.updateConversationReferenceById(user.userId, conversationReference);
let conversationId = step.context.activity.conversation.id;
let pid = GBMinService.pidsConversation[conversationId];
if (!pid) {
pid = step.context.activity['pid'];
if (!pid) {
pid = WhatsappDirectLine.pidByNumber[context.activity.from.id];
if (!pid) {
pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null, step);
}
}
}
GBMinService.pidsConversation[conversationId] = pid;
step.context.activity['pid'] = pid;
const auth = min.core.getParam(min.instance, 'Enable Authentication', false);
if (auth) {
const res = await t.find({ pid: pid, handle: 'users.csv', args: [`key={member.id}`] });
if (!res) {
await min.conversationalService.sendText(min, step, 'Sorry, not authorized.');
res.end();
}
}
// First time processing.
@ -1234,18 +1105,34 @@ export class GBMinService {
if (step.context.activity.channelId !== 'msteams') {
const service = new KBService(min.core.sequelize);
const data = await service.getFaqBySubjectArray(min.instance.instanceId, 'faq', undefined);
if (data.length > 0) {
await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'bullet',
data: data.slice(0, 10)
});
await min.conversationalService.sendEvent(min, step, 'play', {
playerType: 'bullet',
data: data.slice(0, 10)
});
}
}
let conversationId = step.context.activity.conversation.id;
let pid = GBMinService.pidsConversation[conversationId];
if (!pid) {
pid = step.context.activity['pid'];
if (!pid) {
pid = WhatsappDirectLine.pidByNumber[context.activity.from.id];
if (!pid) {
pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null, step);
}
}
}
GBMinService.pidsConversation[conversationId] = pid;
step.context.activity['pid'] = pid;
const notes = min.core.getParam(min.instance, 'Notes', null);
if (await this.handleUploads(min, step, user, params, notes != null)) {
return;
}
// Required for MSTEAMS handling of persisted conversations.
@ -1259,6 +1146,7 @@ export class GBMinService {
);
const botToken = await credentials.getToken();
const headers = { Authorization: `Bearer ${botToken}` };
const t = new SystemKeywords();
const data = await t.getByHttp({
pid: 0,
url: file.contentUrl,
@ -1283,8 +1171,6 @@ export class GBMinService {
if (!(await sec.getParam(user, 'welcomed'))) {
const startDialog = min.core.getParam(min.instance, 'Start Dialog', null);
if (startDialog) {
const t = new SystemKeywords();
t.setMemoryContext({ pid: pid, erase: true });
await sec.setParam(userId, 'welcomed', 'true');
GBLogEx.info(
min,
@ -1303,37 +1189,53 @@ export class GBMinService {
if (context.activity.type === 'installationUpdate') {
GBLogEx.info(min, `Bot installed on Teams.`);
} else if (context.activity.type === 'conversationUpdate') {
// Calls onNewSession event on each .gbapp package.
} else if (context.activity.type === 'conversationUpdate' &&
context.activity.membersAdded.length > 0) {
// Check if a bot or a human participant is being added to the conversation.
await CollectionUtil.asyncForEach(appPackages, async e => {
await e.onNewSession(min, step);
});
const member = context.activity.membersAdded[0];
if (context.activity.membersAdded[0].id === context.activity.recipient.id) {
GBLogEx.info(min, `Bot added to conversation, starting chat...`);
// Auto starts dialogs if any is specified.
// Calls onNewSession event on each .gbapp package.
if (!startDialog && !(await sec.getParam(user, 'welcomed'))) {
// Otherwise, calls / (root) to default welcome users.
await CollectionUtil.asyncForEach(appPackages, async e => {
await e.onNewSession(min, step);
});
await step.beginDialog('/');
} else {
if (!GBMinService.userMobile(step) && !min['conversationWelcomed'][step.context.activity.conversation.id]) {
const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null, step);
const t = new SystemKeywords();
t.setMemoryContext({ pid: pid, erase: true });
// Auto starts dialogs if any is specified.
step.context.activity['pid'] = pid;
if (!startDialog && !(await sec.getParam(user, 'welcomed'))) {
// Otherwise, calls / (root) to default welcome users.
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
await step.beginDialog('/');
} else {
if (
!GBMinService.userMobile(step) &&
!min['conversationWelcomed'][step.context.activity.conversation.id]
) {
GBLogEx.info(
min,
`Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
const pid = GBVMService.createProcessInfo(user, min, step.context.activity.channelId, null, step);
step.context.activity['pid'] = pid;
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
GBLogEx.info(
min,
`Auto start (web 1) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
}
}
} else {
GBLogEx.info(min, `Person added to conversation: ${member.name}`);
return;
}
} else if (context.activity.type === 'message') {
// Required for F0 handling of persisted conversations.
GBLogEx.info(
@ -1341,6 +1243,7 @@ export class GBMinService {
`Human: pid:${pid} ${context.activity.from.id} ${GBUtil.toYAML(WhatsappDirectLine.pidByNumber)} ${context.activity.text} (type: ${context.activity.type}, name: ${context.activity.name}, channelId: ${context.activity.channelId})`
);
// Processes messages activities.
await this.processMessageActivity(context, min, step, pid);
@ -1363,10 +1266,10 @@ export class GBMinService {
};
try {
if (GBConfigService.get('GB_MODE') !== 'legacy') {
if (!GBConfigService.get('STORAGE_NAME')) {
const context = adapter['createContext'](req);
context['_activity'] = context.activity.body;
await adapter['processActivity'](req, res, handler);
await handler(context);
// Return status
res.status(200);
@ -1399,13 +1302,12 @@ export class GBMinService {
);
if (context.activity.name === 'showSubjects') {
await step.replaceDialog('/answer', {
query: `Show a list of subjects you can help me in ${contentLocale} language.`
});
await step.replaceDialog('/answer', { query: `Show a list of subjects you can help me in ${contentLocale} language.` });
} else if (context.activity.name === 'showFAQ') {
await step.replaceDialog('/answer', {
query: `Show a FAQ for me about how can you help me in a bullet list, in ${contentLocale} language.`
});
await step.replaceDialog('/answer', { query: `Show a FAQ for me about how can you help me in a bullet list, in ${contentLocale} language.` });
} else if (context.activity.name === 'answerEvent') {
await step.beginDialog('/answerEvent', <AskDialogArgs>{
questionId: context.activity.data,
@ -1491,6 +1393,7 @@ export class GBMinService {
const successfulSaves = await Promise.all(promises);
async function replyForReceivedAttachments(attachmentData) {
if (attachmentData) {
// In case of not having HEAR activated before, it is
// a upload with no Dialog, so run Auto Save to .gbdrive.
@ -1534,16 +1437,16 @@ export class GBMinService {
name: string;
}
const results = (await successfulSaves.reduce(async (accum: GBFile[], item) => {
const results = await successfulSaves.reduce(async (accum: GBFile[], item) => {
const result: GBFile = {
data: await fs.readFile(successfulSaves[0]['filename']),
filename: successfulSaves[0]['filename'],
name: successfulSaves[0]['name'],
url: successfulSaves[0]['url']
url: successfulSaves[0]['url'],
};
accum.push(result);
return accum;
}, [])) as GBFile[];
}, []) as GBFile[];
if (min.cbMap[user.userId] && min.cbMap[user.userId].promise == '!GBHEAR') {
if (results.length > 1) {
@ -1651,15 +1554,15 @@ export class GBMinService {
!step.context.activity['group']
) {
await sec.setParam(userId, 'welcomed', 'true');
const t = new SystemKeywords();
t.setMemoryContext({ pid: pid, erase: true });
min['conversationWelcomed'][step.context.activity.conversation.id] = true;
GBLogEx.info(
min,
`Auto start (4) dialog is now being called: ${startDialog} for ${min.instance.instanceId}...`
);
await GBVMService.callVM(startDialog.toLowerCase(), min, step, pid);
}
}
@ -1713,7 +1616,7 @@ export class GBMinService {
// Removes unwanted chars in input text.
step.context.activity['originalText'] = context.activity.text;
const text = context.activity.text;
const text = await GBConversationalService.handleText(min, user, step, context.activity.text);
step.context.activity['originalText'];
step.context.activity['text'] = text;
@ -1759,9 +1662,8 @@ export class GBMinService {
try {
await step.continueDialog();
} catch (error) {
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${
error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
const msg = `ERROR: ${error.message} ${error.stack} ${error.error ? error.error.body : ''} ${error.error ? (error.error.stack ? error.error.stack : '') : ''
}`;
GBLog.error(msg);
await min.conversationalService.sendText(
min,
@ -1884,10 +1786,10 @@ export class GBMinService {
pingSendTimeout: null,
keepAliveTimeout: null,
listeners: {
unsubscribed(subscriptions: number): void {},
subscribed(subscriptions: number): void {},
disconnected(remoteId: string, connections: number): void {},
connected(remoteId: string, connections: number): void {},
unsubscribed(subscriptions: number): void { },
subscribed(subscriptions: number): void { },
disconnected(remoteId: string, connections: number): void { },
connected(remoteId: string, connections: number): void { },
messageIn(...params): void {
params.shift();
},
@ -1908,7 +1810,7 @@ export class GBMinService {
private mutex: Mutex = new Mutex();
public async watchPackages(min: GBMinInstance, packageType: string): Promise<void> {
if (GBConfigService.get('GB_MODE') !== 'legacy') {
if (!GBConfigService.get('STORAGE_NAME')) {
const packagePath = GBUtil.getGBAIPath(min.botId, packageType);
const libraryPath = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath);

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -99,7 +99,6 @@ export class GBSSR {
'--disable-features=site-per-process',
'--disable-gpu',
'--no-first-run',
'--no-sandbox',
'--no-default-browser-check'
];
@ -118,7 +117,7 @@ export class GBSSR {
return {
args: args,
ignoreHTTPSErrors: true,
headless: process.env.CHROME_HEADLESS === 'true',
headless: false,
defaultViewport: null,
executablePath: process.env.CHROME_PATH ? process.env.CHROME_PATH : executablePath(),
ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection']
@ -336,9 +335,6 @@ export class GBSSR {
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

View file

@ -6,7 +6,7 @@
| ██ ██ █ █ ██ █ █ ██ ██ ██ ██ ██ ██ █ ██ ██ █ █ |
| █████ █████ █ ███ █████ ██ ██ ██ ██ █████ ████ █████ █ ███ |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -22,7 +22,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -38,7 +38,7 @@
<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" />
<script src="./js/webchat.js"></script>
<title>{title} | General Bots</title>
<script>
document.addEventListener('DOMContentLoaded', () => {

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -347,7 +347,7 @@ class GBUIApp extends React.Component {
);
if (this.state.instanceClient) {
let color1 = this.state.instanceClient.color1;
gbCss = <GBCss instance={this.state.instanceClient} />;
seo = <SEO instance={this.state.instanceClient} />;

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,7 +21,7 @@
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.com.br. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
@ -35,7 +35,7 @@ const footer = () => (
<div className="footer-container">
General Bots Community Edition
<br/>
<a href="http://pragmatismo.com.br">pragmatismo.com.br</a>
<a href="http://pragmatismo.cloud">pragmatismo.cloud</a>
</div>
);
export default footer

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
| |
| |
| |
| General Bots Copyright (c) pragmatismo.com.br. All rights reserved. |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
@ -21,58 +21,66 @@
| 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. |
| "General Bots" is a registered trademark of pragmatismo.cloud. |
| The licensing of the program under the AGPLv3 does not imply a |
| trademark license. Therefore any rights, title and interest in |
| our trademarks remain entirely with us. |
| |
\*****************************************************************************/
import React from 'react';
import React from "react";
class SideBarMenu extends React.Component {
send(command) {
window.botConnection.postActivity({
type: 'event',
name: command,
locale: 'en-us',
textFormat: 'plain',
timestamp: new Date().toISOString()
});
window.botConnection
.postActivity({
type: "event",
name: command,
locale: "en-us",
textFormat: "plain",
timestamp: new Date().toISOString()
});
}
render() {
return (
<div>
<div className="titleSideBarMenu">
<img className="pragmatismoLogo" width="64px" src={this.props.instance.logo} alt="General Bots Logo" />
<img
className="pragmatismoLogo"
src={this.props.instance.logo}
alt="General Bots Logo" />
</div>
<div className="SidebarMenu">
<div className="IconsMenu">
<div className="iconMenu">
<span className="iconText" onClick={() => this.send('showFAQ')}>
<span className="iconText" onClick={() => this.send("showFAQ")}>
FAQ
</span>
</div>
<div className="iconMenu">
<span
className="iconText"
onClick={() =>
window.open(`https://drive.pragmatismo.com.br/browser/${this.props.instance.botId}.gbai`)
}
<span className="iconText"
onClick={() => window.open(`https://pragmatismo.sharepoint.com/sites/bots/Online/${this.props.instance.botId}.gbai`)}
>
Drive
</span>
</div>
<div className="iconMenu">
<span className="iconText" onClick={() => this.send('showSubjects')}>
<span
className="iconText"
onClick={() => this.send("showSubjects")}>
Subjects
</span>
</div>
<div className="iconMenu">
<span className="iconText" onClick={() => window.open('mailto:talk@pragmatismo.com.br')}>
<span
className="iconText"
onClick={() => window.open('mailto:talk@pragmatismo.cloud')}
>
Suggestions
</span>
</div>
</div>
</div>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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