Compare commits

..

14 commits
master ... main

Author SHA1 Message Date
7928c0ef14 refactor: enterprise-grade error handling, HTTP client, models with builder patterns 2025-12-20 19:57:51 -03:00
b7b313c2e9 docs: update PROMPT.md with weekly maintenance checklist 2025-12-18 16:18:14 -03:00
ffcfe9bbcb Disable auto-trigger CI - manual only via workflow_dispatch 2025-12-16 17:07:34 -03:00
b61f05b96d Revert "Disable CI - library compiled as dependency"
Some checks failed
GBCI / build (push) Failing after 4m4s
This reverts commit 9ae01eb90c.
2025-12-16 17:04:57 -03:00
9ae01eb90c Disable CI - library compiled as dependency 2025-12-16 17:02:21 -03:00
0e1903f419 Add Cargo caching for faster builds
Some checks failed
GBCI / build (push) Failing after 5m52s
2025-12-16 16:35:23 -03:00
3303ae4be0 Add Forgejo CI workflow for botlib - Compile Rust library
Some checks failed
GBCI / build (push) Failing after 4m20s
2025-12-16 13:25:15 -03:00
f9582d3c7b Lowercase botserver/botbook references 2025-12-12 23:21:18 -03:00
90599c2e7c Update http_client.rs 2025-12-12 16:39:44 -03:00
ea3aa1413f chore: Replace emoji status indicators with text equivalents 2025-12-09 07:55:33 -03:00
34152dabc3 fix: change default botserver port from 8081 to 8080
Match botserver's default port configuration for proper
health check connectivity between botui and botserver.
2025-12-08 14:08:56 -03:00
b159b4c00a Add Default impl for BotResponse to support testing 2025-12-05 16:43:48 -03:00
97b897a557 Remove VERSION.md 2025-12-04 12:28:10 -03:00
dfa904042e - To Rust. 2025-12-03 19:45:09 -03:00
47 changed files with 4500 additions and 6617 deletions

View file

@ -0,0 +1,54 @@
name: GBCI
on:
workflow_dispatch:
# Disabled auto-trigger - enable when needed
# 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: Cache Cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-botlib-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-botlib-
- name: Install Rust
uses: msrd0/rust-toolchain@v1
with:
toolchain: stable
- name: Build library (default features)
run: cargo build --locked
- name: Build library (full features)
run: cargo build --locked --features full
- name: Run tests
run: cargo test --locked --features full
- name: Build release
run: cargo build --locked --release --features full
- name: Deploy library
run: |
sudo mkdir -p /opt/gbo/lib/botlib
sudo cp ./target/release/libbotlib.rlib /opt/gbo/lib/botlib/ || true
sudo cp ./target/release/libbotlib.a /opt/gbo/lib/botlib/ || true
sudo cp ./target/release/libbotlib.so /opt/gbo/lib/botlib/ || true

20
.gitignore vendored
View file

@ -1,5 +1,15 @@
node_modules
docs
dist
xx.xx
xx.yy
.tmp*
.tmp/*
*.log
target*
.env
target
*.env
work
*.out
bin
botserver-stack
*logfile*
*-log*
docs/book
*.rdb

View file

@ -1,2 +0,0 @@
node_modules
docs

View file

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

2119
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

38
Cargo.toml Normal file
View file

@ -0,0 +1,38 @@
[package]
name = "botlib"
version = "6.1.0"
edition = "2021"
description = "Shared library for General Bots - common types, utilities, and HTTP client"
license = "AGPL-3.0"
authors = ["Pragmatismo.com.br", "General Bots Community"]
repository = "https://github.com/GeneralBots/BotServer"
[features]
default = []
full = ["database", "http-client", "validation"]
database = ["dep:diesel"]
http-client = ["dep:reqwest"]
validation = ["dep:validator"]
[dependencies]
# Core
anyhow = "1.0"
thiserror = "2.0"
log = "0.4"
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.11", features = ["serde", "v4"] }
toml = "0.8"
# Optional: Database
diesel = { version = "2.1", features = ["postgres", "uuid", "chrono", "serde_json", "r2d2"], optional = true }
# Optional: HTTP Client
reqwest = { version = "0.12", features = ["json"], optional = true }
# Optional: Validation
validator = { version = "0.18", features = ["derive"], optional = true }
[dev-dependencies]
tokio = { version = "1.41", features = ["rt", "macros"] }

300
PROMPT.md Normal file
View file

@ -0,0 +1,300 @@
# BotLib Development Prompt Guide
**Version:** 6.1.0
**Purpose:** LLM context for BotLib shared library development
---
## Version Management - CRITICAL
**Current version is 6.1.0 - DO NOT CHANGE without explicit approval!**
### Rules
1. **Version is 6.1.0 across ALL workspace crates**
2. **NEVER change version without explicit user approval**
3. **All workspace crates share version 6.1.0**
4. **BotLib does not have migrations - all migrations are in botserver/**
---
## Official Icons - Reference
**BotLib does not contain icons.** Icons are managed in:
- `botui/ui/suite/assets/icons/` - Runtime UI icons
- `botbook/src/assets/icons/` - Documentation icons
When documenting or referencing UI elements in BotLib:
- Reference icons by name (e.g., `gb-chat.svg`, `gb-drive.svg`)
- Never generate or embed icon content
- See `botui/PROMPT.md` for the complete icon list
---
## Weekly Maintenance - EVERY MONDAY
### Package Review Checklist
**Every Monday, review the following:**
1. **Dependency Updates**
```bash
cargo outdated
cargo audit
```
2. **Package Consolidation Opportunities**
- Check if new crates can replace custom code
- Look for crates that combine multiple dependencies
- Review `Cargo.toml` for redundant dependencies
3. **Code Reduction Candidates**
- Custom implementations that now have crate equivalents
- Boilerplate that can be replaced with derive macros
- Re-exports that can simplify downstream usage
4. **Feature Flag Review**
- Check if optional features are still needed
- Consolidate similar features
- Remove unused feature gates
### Packages to Watch
| Area | Potential Packages | Purpose |
|------|-------------------|---------|
| Error Handling | `anyhow`, `thiserror` | Consolidate error types |
| Validation | `validator` | Replace manual validation |
| Serialization | `serde` derives | Reduce boilerplate |
| UUID | `uuid` | Consistent ID generation |
---
## Project Overview
BotLib is the shared foundation library for the General Bots workspace. It provides common types, utilities, error handling, and optional integrations that are consumed by botserver, botui, and botapp.
### Workspace Position
```
botlib/ # THIS PROJECT - Shared library
botserver/ # Main server (depends on botlib)
botui/ # Web/Desktop UI (depends on botlib)
botapp/ # Desktop app (depends on botlib)
botbook/ # Documentation
```
### What BotLib Provides
- **Error Types**: Common error handling with anyhow/thiserror
- **Models**: Shared data structures and types
- **HTTP Client**: Optional reqwest wrapper
- **Database**: Optional diesel integration
- **Validation**: Optional input validation
- **Branding**: Version and branding constants
---
## Feature Flags
```toml
[features]
default = []
full = ["database", "http-client", "validation"]
database = ["dep:diesel"]
http-client = ["dep:reqwest"]
validation = ["dep:validator"]
```
### Usage in Dependent Crates
```toml
# botserver/Cargo.toml
[dependencies.botlib]
path = "../botlib"
features = ["database"]
# botui/Cargo.toml
[dependencies.botlib]
path = "../botlib"
features = ["http-client"]
```
---
## Code Generation Rules
### CRITICAL REQUIREMENTS
```
- Library code must be generic and reusable
- No hardcoded values or project-specific logic
- All public APIs must be well-documented
- Feature gates for optional dependencies
- Zero warnings - clean compilation required
```
### Module Structure
```
src/
├── lib.rs # Public exports, feature gates
├── error.rs # Error types (thiserror)
├── models.rs # Shared data models
├── message_types.rs # Message type definitions
├── http_client.rs # HTTP client wrapper (feature-gated)
├── branding.rs # Version, branding constants
└── version.rs # Version information
```
---
## Adding New Features
### Adding a Shared Type
```rust
// src/models.rs
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SharedEntity {
pub id: Uuid,
pub name: String,
pub created_at: DateTime<Utc>,
}
```
### Adding a Feature-Gated Module
```rust
// src/lib.rs
#[cfg(feature = "my-feature")]
pub mod my_module;
#[cfg(feature = "my-feature")]
pub use my_module::MyType;
```
```toml
# Cargo.toml
[features]
my-feature = ["dep:some-crate"]
[dependencies]
some-crate = { version = "1.0", optional = true }
```
### Adding Error Types
```rust
// src/error.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum BotLibError {
#[error("Configuration error: {0}")]
Config(String),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Database error: {0}")]
Database(String),
}
pub type Result<T> = std::result::Result<T, BotLibError>;
```
---
## Re-exports Strategy
BotLib should re-export common dependencies to ensure version consistency:
```rust
// src/lib.rs
pub use anyhow;
pub use chrono;
pub use serde;
pub use serde_json;
pub use thiserror;
pub use uuid;
#[cfg(feature = "database")]
pub use diesel;
#[cfg(feature = "http-client")]
pub use reqwest;
```
Consumers then use:
```rust
use botlib::uuid::Uuid;
use botlib::chrono::Utc;
```
---
## Dependencies
| Library | Version | Purpose | Optional |
|---------|---------|---------|----------|
| anyhow | 1.0 | Error handling | No |
| thiserror | 2.0 | Error derive | No |
| log | 0.4 | Logging facade | No |
| chrono | 0.4 | Date/time | No |
| serde | 1.0 | Serialization | No |
| serde_json | 1.0 | JSON | No |
| uuid | 1.11 | UUIDs | No |
| toml | 0.8 | Config parsing | No |
| diesel | 2.1 | Database ORM | Yes |
| reqwest | 0.12 | HTTP client | Yes |
| validator | 0.18 | Validation | Yes |
---
## Testing
```bash
# Test all features
cargo test --all-features
# Test specific feature
cargo test --features database
# Test without optional features
cargo test
```
---
## Final Checks Before Commit
```bash
# Verify version is 6.1.0
grep "^version" Cargo.toml | grep "6.1.0"
# Build with all features
cargo build --all-features
# Check for warnings
cargo check --all-features 2>&1 | grep warning
# Run tests
cargo test --all-features
```
---
## Rules
- Keep botlib minimal and focused
- No business logic - only utilities and types
- Feature gate all optional dependencies
- Maintain backward compatibility
- Document all public APIs
- Target zero warnings
- **Version**: Always 6.1.0 - do not change without approval

View file

@ -1,3 +1,3 @@
General Bots® base library for building Node.js TypeScript Apps packages (.gbapp).
See: https://github.com/pragmatismo-io/BotServer for main documentation.
See: https://github.com/pragmatismo-io/botserver for main documentation.

View file

@ -1,107 +0,0 @@
# History
## Version 0.1.6
* References and version updated.
## Version 0.1.5
* Docs updated.
## Version 0.1.4
* New fields to 100% automated development environement setup.
## Version 0.1.3
- FIX: Updated dependencies versions.
## Version 0.1.2
- NEW: pragmatismo-io-framework updated.
## Version 0.1.0
- NEW: Migration to Bot Framework v4.
## Version 0.0.33
- FIX: Updated dependencies versions.
## Version 0.0.32
- FIX: Updated dependencies versions.
## Version 0.0.31
- FIX: Export of Sequelize from sequelize-typescript.
## Version 0.0.30
- FIX: Packages updated.
- FIX: Missing some 'use strict'.
## Version 0.0.29
- FIX: Packages updated.
## Version 0.0.28
- FIX: Package compiled.
## Version 0.0.27
- FIX: Merged changes.
## Version 0.0.26
- NEW: Support for Speech recognition/synthesis.
## Version 0.0.25
- FIX: Packages updated.
## Version 0.0.24
- FIX: Packages updated.
## Version 0.0.23
- FIX: Trying to remove botbuilder dependency on hoek vunerability with no success, MS is promissing update it: https://github.com/Microsoft/BotBuilder/issues/4206.
## Version 0.0.22
- FIX: Packages updated.
## Version 0.0.21
- FIX: Whatsapp directline client improved.
## Version 0.0.20
- NEW: Whatsapp directline client is now working in preview.
- NEW: Parameter whatsappServiceWebhookUrl added.
## Version 0.0.19
- NEW: Parameter whatsappServiceUrl added to support other whatsapp channels.
## Version 0.0.18
- FIX: Missing TS compilation.
## Version 0.0.17
- NEW: The bot now has an associated mobile Whatsapp number;
## Version 0.0.16
- FIX: Compilation of Typescript into JavaScript.
## Version 0.0.15
- NEW: Now each .gbapp has it own set of syspackages loaded.
## Version 0.0.14
- NEW: Added support for Whatsapp external service key on bot instance model.

View file

@ -1,865 +0,0 @@
/*! normalize.css v1.1.3 | MIT License | git.io/normalize */
/* ========================================================================== HTML5 display definitions ========================================================================== */
/** Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. */
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; }
/** Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. */
audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; }
/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */
audio:not([controls]) { display: none; height: 0; }
/** Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. Known issue: no IE 6 support. */
[hidden] { display: none; }
/* ========================================================================== Base ========================================================================== */
/** 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using `em` units. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */
html { font-size: 100%; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ font-family: sans-serif; }
/** Address `font-family` inconsistency between `textarea` and other form elements. */
button, input, select, textarea { font-family: sans-serif; }
/** Address margins handled incorrectly in IE 6/7. */
body { margin: 0; }
/* ========================================================================== Links ========================================================================== */
/** Address `outline` inconsistency between Chrome and other browsers. */
a:focus { outline: thin dotted; }
a:active, a:hover { outline: 0; }
/** Improve readability when focused and also mouse hovered in all browsers. */
/* ========================================================================== Typography ========================================================================== */
/** Address font sizes and margins set differently in IE 6/7. Address font sizes within `section` and `article` in Firefox 4+, Safari 5, and Chrome. */
h1 { font-size: 2em; margin: 0.67em 0; }
h2 { font-size: 1.5em; margin: 0.83em 0; }
h3 { font-size: 1.17em; margin: 1em 0; }
h4, .tsd-index-panel h3 { font-size: 1em; margin: 1.33em 0; }
h5 { font-size: 0.83em; margin: 1.67em 0; }
h6 { font-size: 0.67em; margin: 2.33em 0; }
/** Address styling not present in IE 7/8/9, Safari 5, and Chrome. */
abbr[title] { border-bottom: 1px dotted; }
/** Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */
b, strong { font-weight: bold; }
blockquote { margin: 1em 40px; }
/** Address styling not present in Safari 5 and Chrome. */
dfn { font-style: italic; }
/** Address differences between Firefox and other browsers. Known issue: no IE 6/7 normalization. */
hr { box-sizing: content-box; height: 0; }
/** Address styling not present in IE 6/7/8/9. */
mark { background: #ff0; color: #000; }
/** Address margins set differently in IE 6/7. */
p, pre { margin: 1em 0; }
/** Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */
code, kbd, pre, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em; }
/** Improve readability of pre-formatted text in all browsers. */
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }
/** Address CSS quotes not supported in IE 6/7. */
q { quotes: none; }
q:before, q:after { content: ""; content: none; }
/** Address `quotes` property not supported in Safari 4. */
/** Address inconsistent and variable font size in all browsers. */
small { font-size: 80%; }
/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */
sub { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; top: -0.5em; }
sub { bottom: -0.25em; }
/* ========================================================================== Lists ========================================================================== */
/** Address margins set differently in IE 6/7. */
dl, menu, ol, ul { margin: 1em 0; }
dd { margin: 0 0 0 40px; }
/** Address paddings set differently in IE 6/7. */
menu, ol, ul { padding: 0 0 0 40px; }
/** Correct list images handled incorrectly in IE 7. */
nav ul, nav ol { list-style: none; list-style-image: none; }
/* ========================================================================== Embedded content ========================================================================== */
/** 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 2. Improve image quality when scaled in IE 7. */
img { border: 0; /* 1 */ -ms-interpolation-mode: bicubic; }
/* 2 */
/** Correct overflow displayed oddly in IE 9. */
svg:not(:root) { overflow: hidden; }
/* ========================================================================== Figures ========================================================================== */
/** Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */
figure, form { margin: 0; }
/* ========================================================================== Forms ========================================================================== */
/** Correct margin displayed oddly in IE 6/7. */
/** Define consistent border, margin, and padding. */
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
/** 1. Correct color not being inherited in IE 6/7/8/9. 2. Correct text not wrapping in Firefox 3. 3. Correct alignment displayed oddly in IE 6/7. */
legend { border: 0; /* 1 */ padding: 0; white-space: normal; /* 2 */ *margin-left: -7px; }
/* 3 */
/** 1. Correct font size not being inherited in all browsers. 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, and Chrome. 3. Improve appearance and consistency in all browsers. */
button, input, select, textarea { font-size: 100%; /* 1 */ margin: 0; /* 2 */ vertical-align: baseline; /* 3 */ *vertical-align: middle; }
/* 3 */
/** Address Firefox 3+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
button, input { line-height: normal; }
/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. Correct `select` style inheritance in Firefox 4+ and Opera. */
button, select { text-transform: none; }
/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. 4. Remove inner spacing in IE 7 without affecting normal text inputs. Known issue: inner spacing remains in IE 6. */
button, html input[type="button"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; }
/* 4 */
input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; }
/* 4 */
/** Re-set default cursor for disabled elements. */
button[disabled], html input[disabled] { cursor: default; }
/** 1. Address box sizing set to content-box in IE 8/9. 2. Remove excess padding in IE 8/9. 3. Remove excess padding in IE 7. Known issue: excess padding remains in IE 6. */
input { /* 3 */ }
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ *height: 13px; /* 3 */ *width: 13px; }
input[type="search"] { -webkit-appearance: textfield; /* 1 */ /* 2 */ box-sizing: content-box; }
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
/** Remove inner padding and border in Firefox 3+. */
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
/** 1. Remove default vertical scrollbar in IE 6/7/8/9. 2. Improve readability and alignment in all browsers. */
textarea { overflow: auto; /* 1 */ vertical-align: top; }
/* 2 */
/* ========================================================================== Tables ========================================================================== */
/** Remove most spacing between table cells. */
table { border-collapse: collapse; border-spacing: 0; }
/* Visual Studio-like style based on original C# coloring by Jason Diamond <jason@diamond.name> */
.hljs { display: inline-block; padding: 0.5em; background: white; color: black; }
.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { color: #008000; }
.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { color: #00f; }
.xml .hljs-tag { color: #00f; }
.xml .hljs-tag .hljs-value { color: #00f; }
.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { color: #a31515; }
.ruby .hljs-symbol { color: #a31515; }
.ruby .hljs-symbol .hljs-string { color: #a31515; }
.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { color: #a31515; }
.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { color: #2b91af; }
.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { color: #808080; }
.vhdl .hljs-typename { font-weight: bold; }
.vhdl .hljs-string { color: #666666; }
.vhdl .hljs-literal { color: #a31515; }
.vhdl .hljs-attribute { color: #00b0e8; }
.xml .hljs-attribute { color: #f00; }
.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, .col > :first-child > :first-child, .col-1 > :first-child > :first-child, .col-2 > :first-child > :first-child, .col-3 > :first-child > :first-child, .col-4 > :first-child > :first-child, .col-5 > :first-child > :first-child, .col-6 > :first-child > :first-child, .col-7 > :first-child > :first-child, .col-8 > :first-child > :first-child, .col-9 > :first-child > :first-child, .col-10 > :first-child > :first-child, .col-11 > :first-child > :first-child, .tsd-panel > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child, .col > :first-child > :first-child > :first-child, .col-1 > :first-child > :first-child > :first-child, .col-2 > :first-child > :first-child > :first-child, .col-3 > :first-child > :first-child > :first-child, .col-4 > :first-child > :first-child > :first-child, .col-5 > :first-child > :first-child > :first-child, .col-6 > :first-child > :first-child > :first-child, .col-7 > :first-child > :first-child > :first-child, .col-8 > :first-child > :first-child > :first-child, .col-9 > :first-child > :first-child > :first-child, .col-10 > :first-child > :first-child > :first-child, .col-11 > :first-child > :first-child > :first-child, .tsd-panel > :first-child > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child > :first-child { margin-top: 0; }
.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, .col > :last-child > :last-child, .col-1 > :last-child > :last-child, .col-2 > :last-child > :last-child, .col-3 > :last-child > :last-child, .col-4 > :last-child > :last-child, .col-5 > :last-child > :last-child, .col-6 > :last-child > :last-child, .col-7 > :last-child > :last-child, .col-8 > :last-child > :last-child, .col-9 > :last-child > :last-child, .col-10 > :last-child > :last-child, .col-11 > :last-child > :last-child, .tsd-panel > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child, .col > :last-child > :last-child > :last-child, .col-1 > :last-child > :last-child > :last-child, .col-2 > :last-child > :last-child > :last-child, .col-3 > :last-child > :last-child > :last-child, .col-4 > :last-child > :last-child > :last-child, .col-5 > :last-child > :last-child > :last-child, .col-6 > :last-child > :last-child > :last-child, .col-7 > :last-child > :last-child > :last-child, .col-8 > :last-child > :last-child > :last-child, .col-9 > :last-child > :last-child > :last-child, .col-10 > :last-child > :last-child > :last-child, .col-11 > :last-child > :last-child > :last-child, .tsd-panel > :last-child > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child > :last-child { margin-bottom: 0; }
.container { max-width: 1200px; margin: 0 auto; padding: 0 40px; }
@media (max-width: 640px) { .container { padding: 0 20px; } }
.container-main { padding-bottom: 200px; }
.row { position: relative; margin: 0 -10px; }
.row:after { visibility: hidden; display: block; content: ""; clear: both; height: 0; }
.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { box-sizing: border-box; float: left; padding: 0 10px; }
.col-1 { width: 8.33333%; }
.offset-1 { margin-left: 8.33333%; }
.col-2 { width: 16.66667%; }
.offset-2 { margin-left: 16.66667%; }
.col-3 { width: 25%; }
.offset-3 { margin-left: 25%; }
.col-4 { width: 33.33333%; }
.offset-4 { margin-left: 33.33333%; }
.col-5 { width: 41.66667%; }
.offset-5 { margin-left: 41.66667%; }
.col-6 { width: 50%; }
.offset-6 { margin-left: 50%; }
.col-7 { width: 58.33333%; }
.offset-7 { margin-left: 58.33333%; }
.col-8 { width: 66.66667%; }
.offset-8 { margin-left: 66.66667%; }
.col-9 { width: 75%; }
.offset-9 { margin-left: 75%; }
.col-10 { width: 83.33333%; }
.offset-10 { margin-left: 83.33333%; }
.col-11 { width: 91.66667%; }
.offset-11 { margin-left: 91.66667%; }
.tsd-kind-icon { display: block; position: relative; padding-left: 20px; text-indent: -20px; }
.tsd-kind-icon:before { content: ''; display: inline-block; vertical-align: middle; width: 17px; height: 17px; margin: 0 3px 2px 0; background-image: url(../images/icons.png); }
@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-kind-icon:before { background-image: url(../images/icons@2x.png); background-size: 238px 204px; } }
.tsd-signature.tsd-kind-icon:before { background-position: 0 -153px; }
.tsd-kind-object-literal > .tsd-kind-icon:before { background-position: 0px -17px; }
.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -17px; }
.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -17px; }
.tsd-kind-class > .tsd-kind-icon:before { background-position: 0px -34px; }
.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -34px; }
.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -34px; }
.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -51px; }
.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -51px; }
.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -51px; }
.tsd-kind-interface > .tsd-kind-icon:before { background-position: 0px -68px; }
.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -68px; }
.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -68px; }
.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -85px; }
.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -85px; }
.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -85px; }
.tsd-kind-module > .tsd-kind-icon:before { background-position: 0px -102px; }
.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; }
.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; }
.tsd-kind-external-module > .tsd-kind-icon:before { background-position: 0px -102px; }
.tsd-kind-external-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; }
.tsd-kind-external-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; }
.tsd-kind-enum > .tsd-kind-icon:before { background-position: 0px -119px; }
.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -119px; }
.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -119px; }
.tsd-kind-enum-member > .tsd-kind-icon:before { background-position: 0px -136px; }
.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -136px; }
.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -136px; }
.tsd-kind-signature > .tsd-kind-icon:before { background-position: 0px -153px; }
.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -153px; }
.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -153px; }
.tsd-kind-type-alias > .tsd-kind-icon:before { background-position: 0px -170px; }
.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -170px; }
.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -170px; }
.tsd-kind-variable > .tsd-kind-icon:before { background-position: -136px -0px; }
.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; }
.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; }
.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; }
.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; }
.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; }
.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; }
.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; }
.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; }
.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; }
.tsd-kind-property > .tsd-kind-icon:before { background-position: -136px -0px; }
.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; }
.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; }
.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; }
.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; }
.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; }
.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; }
.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; }
.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; }
.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; }
.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; }
.tsd-kind-get-signature > .tsd-kind-icon:before { background-position: -136px -17px; }
.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -17px; }
.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -17px; }
.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -17px; }
.tsd-kind-set-signature > .tsd-kind-icon:before { background-position: -136px -34px; }
.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -34px; }
.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -34px; }
.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -34px; }
.tsd-kind-accessor > .tsd-kind-icon:before { background-position: -136px -51px; }
.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -51px; }
.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; }
.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -51px; }
.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -51px; }
.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -51px; }
.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -51px; }
.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; }
.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -51px; }
.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -51px; }
.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; }
.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -51px; }
.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -51px; }
.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -68px; }
.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; }
.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; }
.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; }
.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; }
.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; }
.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; }
.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; }
.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; }
.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; }
.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -68px; }
.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; }
.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; }
.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; }
.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; }
.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; }
.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; }
.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; }
.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; }
.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; }
.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -68px; }
.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; }
.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; }
.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; }
.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; }
.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; }
.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; }
.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; }
.tsd-kind-constructor > .tsd-kind-icon:before { background-position: -136px -102px; }
.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; }
.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; }
.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; }
.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; }
.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; }
.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; }
.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; }
.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; }
.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; }
.tsd-kind-constructor-signature > .tsd-kind-icon:before { background-position: -136px -102px; }
.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; }
.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; }
.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; }
.tsd-kind-index-signature > .tsd-kind-icon:before { background-position: -136px -119px; }
.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -119px; }
.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -119px; }
.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -119px; }
.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -136px; }
.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -136px; }
.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; }
.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -136px; }
.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -136px; }
.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -136px; }
.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -136px; }
.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; }
.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -136px; }
.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -136px; }
.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; }
.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -136px; }
.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -136px; }
.tsd-is-static > .tsd-kind-icon:before { background-position: -136px -153px; }
.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -153px; }
.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; }
.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -153px; }
.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -153px; }
.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -153px; }
.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -153px; }
.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; }
.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -153px; }
.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -153px; }
.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; }
.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -153px; }
.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -153px; }
.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -170px; }
.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; }
.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; }
.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; }
.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -170px; }
.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; }
.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; }
.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; }
.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; }
.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; }
.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -187px; }
.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -187px; }
.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -187px; }
.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -187px; }
.no-transition { transition: none !important; }
@-webkit-keyframes fade-in { from { opacity: 0; }
to { opacity: 1; } }
@keyframes fade-in { from { opacity: 0; }
to { opacity: 1; } }
@-webkit-keyframes fade-out { from { opacity: 1; visibility: visible; }
to { opacity: 0; } }
@keyframes fade-out { from { opacity: 1; visibility: visible; }
to { opacity: 0; } }
@-webkit-keyframes fade-in-delayed { 0% { opacity: 0; }
33% { opacity: 0; }
100% { opacity: 1; } }
@keyframes fade-in-delayed { 0% { opacity: 0; }
33% { opacity: 0; }
100% { opacity: 1; } }
@-webkit-keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; }
66% { opacity: 0; }
100% { opacity: 0; } }
@keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; }
66% { opacity: 0; }
100% { opacity: 0; } }
@-webkit-keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); }
to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } }
@keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); }
to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } }
@-webkit-keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); }
to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } }
@keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); }
to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } }
@-webkit-keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); }
to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } }
@keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); }
to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } }
@-webkit-keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; }
to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } }
@keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; }
to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } }
body { background: #fdfdfd; font-family: "Segoe UI", sans-serif; font-size: 16px; color: #222; }
a { color: #4da6ff; text-decoration: none; }
a:hover { text-decoration: underline; }
code, pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0.2em; margin: 0; font-size: 14px; background-color: rgba(0, 0, 0, 0.04); }
pre { padding: 10px; }
pre code { padding: 0; font-size: 100%; background-color: transparent; }
.tsd-typography { line-height: 1.333em; }
.tsd-typography ul { list-style: square; padding: 0 0 0 20px; margin: 0; }
.tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { font-size: 1em; margin: 0; }
.tsd-typography h5, .tsd-typography h6 { font-weight: normal; }
.tsd-typography p, .tsd-typography ul, .tsd-typography ol { margin: 1em 0; }
@media (min-width: 901px) and (max-width: 1024px) { html.default .col-content { width: 72%; }
html.default .col-menu { width: 28%; }
html.default .tsd-navigation { padding-left: 10px; } }
@media (max-width: 900px) { html.default .col-content { float: none; width: 100%; }
html.default .col-menu { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; z-index: 1024; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important; width: 100%; padding: 20px 20px 0 0; max-width: 450px; visibility: hidden; background-color: #fff; -webkit-transform: translate(100%, 0); transform: translate(100%, 0); }
html.default .col-menu > *:last-child { padding-bottom: 20px; }
html.default .overlay { content: ""; display: block; position: fixed; z-index: 1023; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.75); visibility: hidden; }
html.default.to-has-menu .overlay { -webkit-animation: fade-in 0.4s; animation: fade-in 0.4s; }
html.default.to-has-menu header, html.default.to-has-menu footer, html.default.to-has-menu .col-content { -webkit-animation: shift-to-left 0.4s; animation: shift-to-left 0.4s; }
html.default.to-has-menu .col-menu { -webkit-animation: pop-in-from-right 0.4s; animation: pop-in-from-right 0.4s; }
html.default.from-has-menu .overlay { -webkit-animation: fade-out 0.4s; animation: fade-out 0.4s; }
html.default.from-has-menu header, html.default.from-has-menu footer, html.default.from-has-menu .col-content { -webkit-animation: unshift-to-left 0.4s; animation: unshift-to-left 0.4s; }
html.default.from-has-menu .col-menu { -webkit-animation: pop-out-to-right 0.4s; animation: pop-out-to-right 0.4s; }
html.default.has-menu body { overflow: hidden; }
html.default.has-menu .overlay { visibility: visible; }
html.default.has-menu header, html.default.has-menu footer, html.default.has-menu .col-content { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); }
html.default.has-menu .col-menu { visibility: visible; -webkit-transform: translate(0, 0); transform: translate(0, 0); } }
.tsd-page-title { padding: 70px 0 20px 0; margin: 0 0 40px 0; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); }
.tsd-page-title h1 { margin: 0; }
.tsd-breadcrumb { margin: 0; padding: 0; color: #808080; }
.tsd-breadcrumb a { color: #808080; text-decoration: none; }
.tsd-breadcrumb a:hover { text-decoration: underline; }
.tsd-breadcrumb li { display: inline; }
.tsd-breadcrumb li:after { content: " / "; }
html.minimal .container { margin: 0; }
html.minimal .container-main { padding-top: 50px; padding-bottom: 0; }
html.minimal .content-wrap { padding-left: 300px; }
html.minimal .tsd-navigation { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; box-sizing: border-box; z-index: 1; left: 0; top: 40px; bottom: 0; width: 300px; padding: 20px; margin: 0; }
html.minimal .tsd-member .tsd-member { margin-left: 0; }
html.minimal .tsd-page-toolbar { position: fixed; z-index: 2; }
html.minimal #tsd-filter .tsd-filter-group { right: 0; -webkit-transform: none; transform: none; }
html.minimal footer { background-color: transparent; }
html.minimal footer .container { padding: 0; }
html.minimal .tsd-generator { padding: 0; }
@media (max-width: 900px) { html.minimal .tsd-navigation { display: none; }
html.minimal .content-wrap { padding-left: 0; } }
dl.tsd-comment-tags { overflow: hidden; }
dl.tsd-comment-tags dt { clear: both; float: left; padding: 1px 5px; margin: 0 10px 0 0; border-radius: 4px; border: 1px solid #808080; color: #808080; font-size: 0.8em; font-weight: normal; }
dl.tsd-comment-tags dd { margin: 0 0 10px 0; }
dl.tsd-comment-tags p { margin: 0; }
.tsd-panel.tsd-comment .lead { font-size: 1.1em; line-height: 1.333em; margin-bottom: 2em; }
.tsd-panel.tsd-comment .lead:last-child { margin-bottom: 0; }
.toggle-protected .tsd-is-private { display: none; }
.toggle-public .tsd-is-private, .toggle-public .tsd-is-protected, .toggle-public .tsd-is-private-protected { display: none; }
.toggle-inherited .tsd-is-inherited { display: none; }
.toggle-only-exported .tsd-is-not-exported { display: none; }
.toggle-externals .tsd-is-external { display: none; }
#tsd-filter { position: relative; display: inline-block; height: 40px; vertical-align: bottom; }
.no-filter #tsd-filter { display: none; }
#tsd-filter .tsd-filter-group { display: inline-block; height: 40px; vertical-align: bottom; white-space: nowrap; }
#tsd-filter input { display: none; }
@media (max-width: 900px) { #tsd-filter .tsd-filter-group { display: block; position: absolute; top: 40px; right: 20px; height: auto; background-color: #fff; visibility: hidden; -webkit-transform: translate(50%, 0); transform: translate(50%, 0); box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); }
.has-options #tsd-filter .tsd-filter-group { visibility: visible; }
.to-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-in 0.2s; animation: fade-in 0.2s; }
.from-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-out 0.2s; animation: fade-out 0.2s; }
#tsd-filter label, #tsd-filter .tsd-select { display: block; padding-right: 20px; } }
footer { border-top: 1px solid #eee; background-color: #fff; }
footer.with-border-bottom { border-bottom: 1px solid #eee; }
footer .tsd-legend-group { font-size: 0; }
footer .tsd-legend { display: inline-block; width: 25%; padding: 0; font-size: 16px; list-style: none; line-height: 1.333em; vertical-align: top; }
@media (max-width: 900px) { footer .tsd-legend { width: 50%; } }
.tsd-hierarchy { list-style: square; padding: 0 0 0 20px; margin: 0; }
.tsd-hierarchy .target { font-weight: bold; }
.tsd-index-panel .tsd-index-content { margin-bottom: -30px !important; }
.tsd-index-panel .tsd-index-section { margin-bottom: 30px !important; }
.tsd-index-panel h3 { margin: 0 -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; }
.tsd-index-panel ul.tsd-index-list { -webkit-column-count: 3; -moz-column-count: 3; -ms-column-count: 3; -o-column-count: 3; column-count: 3; -webkit-column-gap: 20px; -moz-column-gap: 20px; -ms-column-gap: 20px; -o-column-gap: 20px; column-gap: 20px; padding: 0; list-style: none; line-height: 1.333em; }
@media (max-width: 900px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 1; -moz-column-count: 1; -ms-column-count: 1; -o-column-count: 1; column-count: 1; } }
@media (min-width: 901px) and (max-width: 1024px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 2; -moz-column-count: 2; -ms-column-count: 2; -o-column-count: 2; column-count: 2; } }
.tsd-index-panel ul.tsd-index-list li { -webkit-column-break-inside: avoid; -moz-column-break-inside: avoid; -ms-column-break-inside: avoid; -o-column-break-inside: avoid; column-break-inside: avoid; -webkit-page-break-inside: avoid; -moz-page-break-inside: avoid; -ms-page-break-inside: avoid; -o-page-break-inside: avoid; page-break-inside: avoid; }
.tsd-index-panel a, .tsd-index-panel .tsd-parent-kind-module a { color: #9600ff; }
.tsd-index-panel .tsd-parent-kind-interface a { color: #7da01f; }
.tsd-index-panel .tsd-parent-kind-enum a { color: #cc9900; }
.tsd-index-panel .tsd-parent-kind-class a { color: #4da6ff; }
.tsd-index-panel .tsd-kind-module a { color: #9600ff; }
.tsd-index-panel .tsd-kind-interface a { color: #7da01f; }
.tsd-index-panel .tsd-kind-enum a { color: #cc9900; }
.tsd-index-panel .tsd-kind-class a { color: #4da6ff; }
.tsd-index-panel .tsd-is-private a { color: #808080; }
.tsd-flag { display: inline-block; padding: 1px 5px; border-radius: 4px; color: #fff; background-color: #808080; text-indent: 0; font-size: 14px; font-weight: normal; }
.tsd-anchor { position: absolute; top: -100px; }
.tsd-member { position: relative; }
.tsd-member .tsd-anchor + h3 { margin-top: 0; margin-bottom: 0; border-bottom: none; }
.tsd-navigation { padding: 0 0 0 40px; }
.tsd-navigation a { display: block; padding-top: 2px; padding-bottom: 2px; border-left: 2px solid transparent; color: #222; text-decoration: none; transition: border-left-color 0.1s; }
.tsd-navigation a:hover { text-decoration: underline; }
.tsd-navigation ul { margin: 0; padding: 0; list-style: none; }
.tsd-navigation li { padding: 0; }
.tsd-navigation.primary { padding-bottom: 40px; }
.tsd-navigation.primary a { display: block; padding-top: 6px; padding-bottom: 6px; }
.tsd-navigation.primary ul li a { padding-left: 5px; }
.tsd-navigation.primary ul li li a { padding-left: 25px; }
.tsd-navigation.primary ul li li li a { padding-left: 45px; }
.tsd-navigation.primary ul li li li li a { padding-left: 65px; }
.tsd-navigation.primary ul li li li li li a { padding-left: 85px; }
.tsd-navigation.primary ul li li li li li li a { padding-left: 105px; }
.tsd-navigation.primary > ul { border-bottom: 1px solid #eee; }
.tsd-navigation.primary li { border-top: 1px solid #eee; }
.tsd-navigation.primary li.current > a { font-weight: bold; }
.tsd-navigation.primary li.label span { display: block; padding: 20px 0 6px 5px; color: #808080; }
.tsd-navigation.primary li.globals + li > span, .tsd-navigation.primary li.globals + li > a { padding-top: 20px; }
.tsd-navigation.secondary ul { transition: opacity 0.2s; }
.tsd-navigation.secondary ul li a { padding-left: 25px; }
.tsd-navigation.secondary ul li li a { padding-left: 45px; }
.tsd-navigation.secondary ul li li li a { padding-left: 65px; }
.tsd-navigation.secondary ul li li li li a { padding-left: 85px; }
.tsd-navigation.secondary ul li li li li li a { padding-left: 105px; }
.tsd-navigation.secondary ul li li li li li li a { padding-left: 125px; }
.tsd-navigation.secondary ul.current a { border-left-color: #eee; }
.tsd-navigation.secondary li.focus > a, .tsd-navigation.secondary ul.current li.focus > a { border-left-color: #000; }
.tsd-navigation.secondary li.current { margin-top: 20px; margin-bottom: 20px; border-left-color: #eee; }
.tsd-navigation.secondary li.current > a { font-weight: bold; }
@media (min-width: 901px) { .menu-sticky-wrap { position: static; }
.no-csspositionsticky .menu-sticky-wrap.sticky { position: fixed; }
.no-csspositionsticky .menu-sticky-wrap.sticky-current { position: fixed; }
.no-csspositionsticky .menu-sticky-wrap.sticky-current ul.before-current, .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.after-current { opacity: 0; }
.no-csspositionsticky .menu-sticky-wrap.sticky-bottom { position: absolute; top: auto !important; left: auto !important; bottom: 0; right: 0; }
.csspositionsticky .menu-sticky-wrap.sticky { position: -webkit-sticky; position: sticky; }
.csspositionsticky .menu-sticky-wrap.sticky-current { position: -webkit-sticky; position: sticky; } }
.tsd-panel { margin: 20px 0; padding: 20px; background-color: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); }
.tsd-panel:empty { display: none; }
.tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { margin: 1.5em -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; }
.tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { margin-bottom: 0; border-bottom: 0; }
.tsd-panel table { display: block; width: 100%; overflow: auto; margin-top: 10px; word-break: normal; word-break: keep-all; }
.tsd-panel table th { font-weight: bold; }
.tsd-panel table th, .tsd-panel table td { padding: 6px 13px; border: 1px solid #ddd; }
.tsd-panel table tr { background-color: #fff; border-top: 1px solid #ccc; }
.tsd-panel table tr:nth-child(2n) { background-color: #f8f8f8; }
.tsd-panel-group { margin: 60px 0; }
.tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { padding-left: 20px; padding-right: 20px; }
#tsd-search { transition: background-color 0.2s; }
#tsd-search .title { position: relative; z-index: 2; }
#tsd-search .field { position: absolute; left: 0; top: 0; right: 40px; height: 40px; }
#tsd-search .field input { box-sizing: border-box; position: relative; top: -50px; z-index: 1; width: 100%; padding: 0 10px; opacity: 0; outline: 0; border: 0; background: transparent; color: #222; }
#tsd-search .field label { position: absolute; overflow: hidden; right: -40px; }
#tsd-search .field input, #tsd-search .title { transition: opacity 0.2s; }
#tsd-search .results { position: absolute; visibility: hidden; top: 40px; width: 100%; margin: 0; padding: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); }
#tsd-search .results li { padding: 0 10px; background-color: #fdfdfd; }
#tsd-search .results li:nth-child(even) { background-color: #fff; }
#tsd-search .results li.state { display: none; }
#tsd-search .results li.current, #tsd-search .results li:hover { background-color: #eee; }
#tsd-search .results a { display: block; }
#tsd-search .results a:before { top: 10px; }
#tsd-search .results span.parent { color: #808080; font-weight: normal; }
#tsd-search.has-focus { background-color: #eee; }
#tsd-search.has-focus .field input { top: 0; opacity: 1; }
#tsd-search.has-focus .title { z-index: 0; opacity: 0; }
#tsd-search.has-focus .results { visibility: visible; }
#tsd-search.loading .results li.state.loading { display: block; }
#tsd-search.failure .results li.state.failure { display: block; }
.tsd-signature { margin: 0 0 1em 0; padding: 10px; border: 1px solid #eee; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; }
.tsd-signature.tsd-kind-icon { padding-left: 30px; }
.tsd-signature.tsd-kind-icon:before { top: 10px; left: 10px; }
.tsd-panel > .tsd-signature { margin-left: -20px; margin-right: -20px; border-width: 1px 0; }
.tsd-panel > .tsd-signature.tsd-kind-icon { padding-left: 40px; }
.tsd-panel > .tsd-signature.tsd-kind-icon:before { left: 20px; }
.tsd-signature-symbol { color: #808080; font-weight: normal; }
.tsd-signature-type { font-style: italic; font-weight: normal; }
.tsd-signatures { padding: 0; margin: 0 0 1em 0; border: 1px solid #eee; }
.tsd-signatures .tsd-signature { margin: 0; border-width: 1px 0 0 0; transition: background-color 0.1s; }
.tsd-signatures .tsd-signature:first-child { border-top-width: 0; }
.tsd-signatures .tsd-signature.current { background-color: #eee; }
.tsd-signatures.active > .tsd-signature { cursor: pointer; }
.tsd-panel > .tsd-signatures { margin-left: -20px; margin-right: -20px; border-width: 1px 0; }
.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { padding-left: 40px; }
.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { left: 20px; }
.tsd-panel > a.anchor + .tsd-signatures { border-top-width: 0; margin-top: -20px; }
ul.tsd-descriptions { position: relative; overflow: hidden; transition: height 0.3s; padding: 0; list-style: none; }
ul.tsd-descriptions.active > .tsd-description { display: none; }
ul.tsd-descriptions.active > .tsd-description.current { display: block; }
ul.tsd-descriptions.active > .tsd-description.fade-in { -webkit-animation: fade-in-delayed 0.3s; animation: fade-in-delayed 0.3s; }
ul.tsd-descriptions.active > .tsd-description.fade-out { -webkit-animation: fade-out-delayed 0.3s; animation: fade-out-delayed 0.3s; position: absolute; display: block; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; }
ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { font-size: 16px; margin: 1em 0 0.5em 0; }
ul.tsd-parameters, ul.tsd-type-parameters { list-style: square; margin: 0; padding-left: 20px; }
ul.tsd-parameters > li.tsd-parameter-siganture, ul.tsd-type-parameters > li.tsd-parameter-siganture { list-style: none; margin-left: -20px; }
ul.tsd-parameters h5, ul.tsd-type-parameters h5 { font-size: 16px; margin: 1em 0 0.5em 0; }
ul.tsd-parameters .tsd-comment, ul.tsd-type-parameters .tsd-comment { margin-top: -0.5em; }
.tsd-sources { font-size: 14px; color: #808080; margin: 0 0 1em 0; }
.tsd-sources a { color: #808080; text-decoration: underline; }
.tsd-sources ul, .tsd-sources p { margin: 0 !important; }
.tsd-sources ul { list-style: none; padding: 0; }
.tsd-page-toolbar { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 40px; color: #333; background: #fff; border-bottom: 1px solid #eee; }
.tsd-page-toolbar a { color: #333; text-decoration: none; }
.tsd-page-toolbar a.title { font-weight: bold; }
.tsd-page-toolbar a.title:hover { text-decoration: underline; }
.tsd-page-toolbar .table-wrap { display: table; width: 100%; height: 40px; }
.tsd-page-toolbar .table-cell { display: table-cell; position: relative; white-space: nowrap; line-height: 40px; }
.tsd-page-toolbar .table-cell:first-child { width: 100%; }
.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { content: ""; display: inline-block; width: 40px; height: 40px; margin: 0 -8px 0 0; background-image: url(../images/widgets.png); background-repeat: no-repeat; text-indent: -1024px; vertical-align: bottom; }
@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { background-image: url(../images/widgets@2x.png); background-size: 320px 40px; } }
.tsd-widget { display: inline-block; overflow: hidden; opacity: 0.6; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; }
.tsd-widget:hover { opacity: 0.8; }
.tsd-widget.active { opacity: 1; background-color: #eee; }
.tsd-widget.no-caption { width: 40px; }
.tsd-widget.no-caption:before { margin: 0; }
.tsd-widget.search:before { background-position: 0 0; }
.tsd-widget.menu:before { background-position: -40px 0; }
.tsd-widget.options:before { background-position: -80px 0; }
.tsd-widget.options, .tsd-widget.menu { display: none; }
@media (max-width: 900px) { .tsd-widget.options, .tsd-widget.menu { display: inline-block; } }
input[type=checkbox] + .tsd-widget:before { background-position: -120px 0; }
input[type=checkbox]:checked + .tsd-widget:before { background-position: -160px 0; }
.tsd-select { position: relative; display: inline-block; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; }
.tsd-select .tsd-select-label { opacity: 0.6; transition: opacity 0.2s; }
.tsd-select .tsd-select-label:before { background-position: -240px 0; }
.tsd-select.active .tsd-select-label { opacity: 0.8; }
.tsd-select.active .tsd-select-list { visibility: visible; opacity: 1; transition-delay: 0s; }
.tsd-select .tsd-select-list { position: absolute; visibility: hidden; top: 40px; left: 0; margin: 0; padding: 0; opacity: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); transition: visibility 0s 0.2s, opacity 0.2s; }
.tsd-select .tsd-select-list li { padding: 0 20px 0 0; background-color: #fdfdfd; }
.tsd-select .tsd-select-list li:before { background-position: 40px 0; }
.tsd-select .tsd-select-list li:nth-child(even) { background-color: #fff; }
.tsd-select .tsd-select-list li:hover { background-color: #eee; }
.tsd-select .tsd-select-list li.selected:before { background-position: -200px 0; }
@media (max-width: 900px) { .tsd-select .tsd-select-list { top: 0; left: auto; right: 100%; margin-right: -5px; }
.tsd-select .tsd-select-label:before { background-position: -280px 0; } }
img { max-width: 100%; }

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,206 +0,0 @@
<!doctype html>
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>botlib</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="assets/js/search.js" data-base=".">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="index.html" class="title">botlib</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
<input type="checkbox" id="tsd-filter-externals" checked />
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
<input type="checkbox" id="tsd-filter-only-exported" />
<label class="tsd-widget" for="tsd-filter-only-exported">Only exported</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<ul class="tsd-breadcrumb">
<li>
<a href="globals.html">Globals</a>
</li>
</ul>
<h1> botlib</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<section class="tsd-panel-group tsd-index-group">
<h2>Index</h2>
<section class="tsd-panel tsd-index-panel">
<div class="tsd-index-content">
<section class="tsd-index-section ">
<h3>External modules</h3>
<ul class="tsd-index-list">
<li class="tsd-kind-external-module"><a href="modules/_gberror_.html" class="tsd-kind-icon">"GBError"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_gbmininstance_.html" class="tsd-kind-icon">"GBMin<wbr>Instance"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_gbservice_.html" class="tsd-kind-icon">"GBService"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_igbadminservice_.html" class="tsd-kind-icon">"IGBAdmin<wbr>Service"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_igbconversationalservice_.html" class="tsd-kind-icon">"IGBConversational<wbr>Service"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_igbcoreservice_.html" class="tsd-kind-icon">"IGBCore<wbr>Service"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_igbdialog_.html" class="tsd-kind-icon">"IGBDialog"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_igbpackage_.html" class="tsd-kind-icon">"IGBPackage"</a></li>
<li class="tsd-kind-external-module tsd-is-external"><a href="modules/_igbinstance_.html" class="tsd-kind-icon">"IGBinstance"</a></li>
<li class="tsd-kind-external-module"><a href="modules/_index_.html" class="tsd-kind-icon">"index"</a></li>
</ul>
</section>
</div>
</section>
</section>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<nav class="tsd-navigation primary">
<ul>
<li class="globals current ">
<a href="globals.html"><em>Globals</em></a>
</li>
<li class="label tsd-is-external">
<span>Internals</span>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gberror_.html">"GBError"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gbmininstance_.html">"GBMin<wbr>Instance"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gbservice_.html">"GBService"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbadminservice_.html">"IGBAdmin<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbconversationalservice_.html">"IGBConversational<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbcoreservice_.html">"IGBCore<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbdialog_.html">"IGBDialog"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbpackage_.html">"IGBPackage"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_index_.html">"index"</a>
</li>
<li class="label tsd-is-external">
<span>Externals</span>
</li>
<li class=" tsd-kind-external-module tsd-is-external">
<a href="modules/_igbinstance_.html">"IGBinstance"</a>
</li>
</ul>
</nav>
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
</ul>
</nav>
</div>
</div>
</div>
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-module"><span class="tsd-kind-icon">Module</span></li>
<li class="tsd-kind-object-literal"><span class="tsd-kind-icon">Object literal</span></li>
<li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li>
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
<li class="tsd-kind-function tsd-has-type-parameter"><span class="tsd-kind-icon">Function with type parameter</span></li>
<li class="tsd-kind-index-signature"><span class="tsd-kind-icon">Index signature</span></li>
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
<li class="tsd-kind-enum-member"><span class="tsd-kind-icon">Enumeration member</span></li>
<li class="tsd-kind-property tsd-parent-kind-enum"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-enum"><span class="tsd-kind-icon">Method</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
<li class="tsd-kind-interface tsd-has-type-parameter"><span class="tsd-kind-icon">Interface with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-interface"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-interface"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-interface"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li>
<li class="tsd-kind-class tsd-has-type-parameter"><span class="tsd-kind-icon">Class with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class"><span class="tsd-kind-icon">Accessor</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-class"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-constructor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static property</span></li>
<li class="tsd-kind-call-signature tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="http://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
<div class="overlay"></div>
<script src="assets/js/main.js"></script>
<script>if (location.protocol == 'file:') document.write('<script src="assets/js/search.js"><' + '/script>');</script>
</body>
</html>

View file

@ -1,188 +0,0 @@
<!doctype html>
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>botlib</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="assets/css/main.css">
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="assets/js/search.js" data-base=".">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="index.html" class="title">botlib</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
<input type="checkbox" id="tsd-filter-externals" checked />
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
<input type="checkbox" id="tsd-filter-only-exported" />
<label class="tsd-widget" for="tsd-filter-only-exported">Only exported</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<ul class="tsd-breadcrumb">
<li>
<a href="globals.html">Globals</a>
</li>
</ul>
<h1> botlib</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<div class="tsd-panel tsd-typography">
<p>General Bots base library for building Node.js TypeScript Apps packages (.gbapp).</p>
<p>See: <a href="https://github.com/pragmatismo-io/BotServer">https://github.com/pragmatismo-io/BotServer</a> for main documentation.</p>
</div>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<nav class="tsd-navigation primary">
<ul>
<li class="globals ">
<a href="globals.html"><em>Globals</em></a>
</li>
<li class="label tsd-is-external">
<span>Internals</span>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gberror_.html">"GBError"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gbmininstance_.html">"GBMin<wbr>Instance"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_gbservice_.html">"GBService"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbadminservice_.html">"IGBAdmin<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbconversationalservice_.html">"IGBConversational<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbcoreservice_.html">"IGBCore<wbr>Service"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbdialog_.html">"IGBDialog"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_igbpackage_.html">"IGBPackage"</a>
</li>
<li class=" tsd-kind-external-module">
<a href="modules/_index_.html">"index"</a>
</li>
<li class="label tsd-is-external">
<span>Externals</span>
</li>
<li class=" tsd-kind-external-module tsd-is-external">
<a href="modules/_igbinstance_.html">"IGBinstance"</a>
</li>
</ul>
</nav>
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
</ul>
</nav>
</div>
</div>
</div>
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-module"><span class="tsd-kind-icon">Module</span></li>
<li class="tsd-kind-object-literal"><span class="tsd-kind-icon">Object literal</span></li>
<li class="tsd-kind-variable"><span class="tsd-kind-icon">Variable</span></li>
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
<li class="tsd-kind-function tsd-has-type-parameter"><span class="tsd-kind-icon">Function with type parameter</span></li>
<li class="tsd-kind-index-signature"><span class="tsd-kind-icon">Index signature</span></li>
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
<li class="tsd-kind-enum-member"><span class="tsd-kind-icon">Enumeration member</span></li>
<li class="tsd-kind-property tsd-parent-kind-enum"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-enum"><span class="tsd-kind-icon">Method</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
<li class="tsd-kind-interface tsd-has-type-parameter"><span class="tsd-kind-icon">Interface with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-interface"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-interface"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-interface"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li>
<li class="tsd-kind-class tsd-has-type-parameter"><span class="tsd-kind-icon">Class with type parameter</span></li>
<li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class"><span class="tsd-kind-icon">Accessor</span></li>
<li class="tsd-kind-index-signature tsd-parent-kind-class"><span class="tsd-kind-icon">Index signature</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-constructor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited constructor</span></li>
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-inherited"><span class="tsd-kind-icon">Inherited accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-protected"><span class="tsd-kind-icon">Protected accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private property</span></li>
<li class="tsd-kind-method tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private method</span></li>
<li class="tsd-kind-accessor tsd-parent-kind-class tsd-is-private"><span class="tsd-kind-icon">Private accessor</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static property</span></li>
<li class="tsd-kind-call-signature tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="http://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
<div class="overlay"></div>
<script src="assets/js/main.js"></script>
<script>if (location.protocol == 'file:') document.write('<script src="assets/js/search.js"><' + '/script>');</script>
</body>
</html>

4265
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,84 +0,0 @@
{
"name": "botlib-legacy",
"version": "5.3.0",
"description": "General Bot base library for building Node.js TypeScript Apps packages (.gbapp) and Libray packages (.gblib)",
"main": "dist/index.js",
"types": "dist/index",
"homepage": "http://www.generalbots.ai",
"contributors": [
"Rodrigo Rodriguez <me@rodrigorodriguez.com>",
"João Antonio Ferreira <joao.parana@gmail.com>",
"Jorge Ramos <jramos@pobox.com>",
"PH <ph.an@outlook.com>",
"Dário Vieira <dario.junior3@gmail.com>"
],
"license": "AGPL-3.0",
"repository": {
"type": "git",
"url": "https://github.com/GeneralBots/BotLib.git"
},
"scripts": {
"clean": "rm -rf node_modules/ dist/",
"build": "npm install && npm run build-lib",
"build-lib": "tsc"
},
"dependencies": {
"async": "3.2.6",
"botbuilder": "4.23.3",
"botbuilder-ai": "4.23.3",
"botbuilder-azure": "4.23.3",
"botbuilder-dialogs": "4.23.3",
"botframework-connector": "4.23.3",
"chrono-node": "2.8.4",
"dotenv-extended": "2.9.0",
"iconv-lite": "0.7.0",
"ms": "2.1.3",
"reflect-metadata": "0.2.2",
"sequelize": "6.37.7",
"sequelize-cli": "6.6.3",
"sequelize-typescript": "2.1.6",
"underscore": "1.13.7",
"winston": "3.17.0"
},
"devDependencies": {
"ts-node": "10.9.2",
"typedoc": "0.28.13",
"typescript": "5.9.2"
},
"overrides": {
"lodash.trimend": "npm:lodash@4.17.21",
"lodash.isequal": "npm:lodash@4.17.21",
"node-domexception": "npm:whatwg-url@^11.0.0",
"csv-database": {
"fast-csv": "4.3.6"
},
"sequelize-typescript": {
"glob": "~9.0.0"
},
"tough-cookie": "4.1.3",
"phin": "3.7.1",
"xmldom": "npm:@xmldom/xmldom@^0.8.10",
"form-data": "2.5.5",
"uuid": "9.0.1",
"har-validator": "5.1.5",
"request": "2.88.2",
"yaeti": "npm:events@^3.3.0",
"text-encoding": "npm:text-encoder-lite@^1.0.1",
"docximager": {
"xml2js": "^0.5.0"
},
"tar-fs": "3.1.0",
"ws": "8.18.3",
"xml2js": "^0.6.2",
"inflight": "npm:lru-cache@^10.2.0",
"rimraf": "6.0.1",
"glob": "11.0.3",
"fstream": "npm:fs-extra@^11.0.0",
"puppeteer": "24.20.0",
"fluent-ffmpeg": "npm:ffmpeg-static@^5.0.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz",
"@nlpjs/xtables": {
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
}
}
}

View file

@ -1,5 +0,0 @@
import { DialogContext } from "botbuilder-dialogs";
export class GBDialogStep extends DialogContext {
}

View file

@ -1,56 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
'use strict'
export enum GBERROR_TYPE {
generalError = 2,
nlpGeneralError = 3,
}
export class GBError {
static createFromCode(GBERROR_TYPE): any { }
getMessageFromErrorCode(type: GBERROR_TYPE) {
if (type === GBERROR_TYPE.nlpGeneralError) {
return `Error accessing NLP, check of the service.`
}
}
e: Error
constructor(e: Error, type: GBERROR_TYPE = GBERROR_TYPE.generalError) {
this.e = e
}
static create(message) {
return new GBError(Error(message))
}
}

View file

@ -1,31 +0,0 @@
const loggers = require('./logger');
export class GBLog {
public static getLogger() {
return loggers;
}
public static error(params): void {
if (loggers[0] && loggers[0].error) loggers[0].error(params);
}
public static warn(params): void {
if (loggers[0] && loggers[0].warn) loggers[0].warn(params);
}
public static info(params): void {
if (loggers[0] && loggers[0].info) loggers[0].info(params);
}
public static debug(params): void {
if (loggers[0] && loggers[0].debug) loggers[0].debug(params);
}
public static verbose(params): void {
if (loggers[0] && loggers[0].verbose) loggers[0].verbose(params);
}
public static silly(params): void {
if (loggers[0] && loggers[0].silly) loggers[0].silly(params);
}
}

View file

@ -1,67 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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 { BotAdapter, UserState } from 'botbuilder';
import { DialogSet } from 'botbuilder-dialogs';
import { IGBInstance } from '.';
import { IGBCoreService } from './IGBCoreService';
import { IGBConversationalService, IGBPackage } from '.';
import { IGBAdminService } from './IGBAdminService';
import { IGBDeployer } from './IGBDeployer';
import { IGBKBService } from './IGBKBService';
/** Minimal services for bot. */
export class GBMinInstance {
packages: IGBPackage[];
appPackages: IGBPackage[];
botId: string;
instance: IGBInstance;
core: IGBCoreService;
conversationalService: IGBConversationalService;
kbService: IGBKBService;
adminService: IGBAdminService;
deployService: IGBDeployer;
bot: BotAdapter;
dialogs: DialogSet;
userState: UserState;
userProfile: any;
whatsAppDirectLine: any;
cbMap: {};
scriptMap: {};
sandBoxMap: {};
gbappServices: Array<any> = [];
constructor() {
this.packages = [];
this.gbappServices = [];
}
}

View file

@ -1,33 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
'use strict'
export class GBService { }

View file

@ -1,47 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
import { IGBInstance } from ".";
import { GBMinInstance } from "./GBMinInstance";
export interface IGBAdminService {
acquireElevatedToken(instanceId): Promise<string>;
updateSecurityInfo(
instanceId: number,
authenticatorTenant: string,
authenticatorAuthorityHostUrl: string
): Promise<IGBInstance>;
getValue(instanceId: number, key: string): Promise<string>;
setValue(instanceId: number, key: string, value: string): Promise<void>;
publish(min: GBMinInstance, packageName: string, republish: boolean): Promise<void>;
}

View file

@ -1,51 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
'use strict';
import { GBMinInstance } from './GBMinInstance';
import { GBDialogStep } from './GBDialogStep';
export interface IGBConversationalService {
prompt(min: GBMinInstance, step: GBDialogStep, text: string);
sendText(min: GBMinInstance, step: GBDialogStep, text: string);
sendEvent(min: GBMinInstance, step: GBDialogStep, name: string, value: Object);
sendFile(min: GBMinInstance, step: GBDialogStep, mobile: string, url: string, caption: string);
sendAudio(min: GBMinInstance, step: GBDialogStep, url: string);
prompt(min: GBMinInstance, step: GBDialogStep, text: string);
sendSms(min: GBMinInstance, mobile: string, text: string);
routeNLP(step: GBDialogStep, min: GBMinInstance, text: string);
getCurrentLanguage(step: GBDialogStep);
getNewMobileCode();
sendMarkdownToMobile(min: GBMinInstance, step: GBDialogStep, mobile: string, text: string);
translate(min: GBMinInstance, text: string, language: string): Promise<string>;
getLanguage(min: GBMinInstance, text: string): Promise<string>;
spellCheck(min: GBMinInstance, text: string): Promise<string>;
}

View file

@ -1,64 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict"
import { Sequelize } from "sequelize-typescript"
import { IGBInstance } from "./IGBInstance"
import { IGBInstallationDeployer } from "./IGBInstallationDeployer";
import { IGBPackage } from "./IGBPackage";
/**
* This interface defines the core service which is shared among
* bot packages so they can have direct access to base services.
*/
export interface IGBCoreService {
sequelize: Sequelize
syncDatabaseStructure()
loadInstances(): Promise<IGBInstance[]>;
deleteInstance(botId: string): Promise<void>;
loadInstanceByBotId(botId: string): Promise<IGBInstance>;
loadInstanceByActivationCode(activationCode: string): Promise<IGBInstance>;
loadInstanceById(instanceId: number): Promise<IGBInstance>;
initStorage(): Promise<any>;
createBootInstance(core: IGBCoreService, installationDeployer: IGBInstallationDeployer, proxyAddress: string);
ensureAdminIsSecured();
loadSysPackages(core: IGBCoreService): Promise<IGBPackage[]>;
ensureProxy(port): Promise<string>;
ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService);
checkStorage(azureDeployer: IGBInstallationDeployer);
saveInstance(fullInstance: any);
loadAllInstances(core: IGBCoreService, azureDeployer: IGBInstallationDeployer, proxyAddress: string);
openBrowserInDevelopment();
installWebHook(isGet: boolean, url: string, callback: any);
setWWWRoot(localPath: string);
setEntryPointDialog(dialogName: string);
getParam<T>(instance: IGBInstance, name: string, defaultValue?: T): any;
}

View file

@ -1,44 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
import { IGBInstance } from ".";
import { GBMinInstance } from "./GBMinInstance";
export interface IGBDeployer {
undeployPackageFromLocalPath(instance: IGBInstance, localPath: string): Promise<void>;
deployPackage(min: GBMinInstance, localPath: string): Promise<void>;
deployBlankBot(botId: string, mobile: string, email: string): Promise<IGBInstance>;
botExists(botId: string): Promise<Boolean>;
rebuildIndex(instance: IGBInstance, searchSchema: any): Promise<void>;
refreshNLPEntity(instance: IGBInstance, listName, listData): Promise<void>;
getBotManifest(instance: IGBInstance): Promise<Buffer>;
}

View file

@ -1,41 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict"
import { BotAdapter } from "botbuilder"
import { GBService } from "./GBService"
export class IGBDialog {
bot: BotAdapter
service: GBService
constructor(bot: BotAdapter) {
}
}

View file

@ -1,57 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
import { IGBInstance } from "./IGBInstance";
export interface IGBInstallationDeployer {
updateBotProxy(botId: string, group: string, endpoint: string);
getSubscriptions(credentials);
getKBSearchSchema(indexName);
openStorageFirewall(groupName, serverName);
createApplication(token: string, name: string);
deployFarm(
proxyAddress: string,
instance: IGBInstance,
credentials,
subscriptionId: string
): Promise<IGBInstance>;
deployToCloud(
title: string,
username: string,
password: string,
cloudLocation: string,
authoringKey: string,
appId: string,
appPassword: string,
subscriptionId: string
);
}

View file

@ -1,101 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
export interface IGBInstance {
botId: string;
whoAmIVideo: string;
botEndpoint: string;
authenticatorTenant: string;
authenticatorAuthorityHostUrl: string;
cloudSubscriptionId: string;
cloudUsername: string;
cloudPassword: string;
cloudLocation: string;
instanceId: number;
title: string;
activationCode: string;
description: string;
version: string;
enabledAdmin: boolean;
engineName: string;
marketplaceId: string;
textAnalyticsKey: string;
textAnalyticsEndpoint: string;
marketplacePassword: string;
webchatKey: string;
googleBotKey: string;
googleChatApiKey: string;
googleChatSubscriptionName: string;
googleClientEmail: string;
googlePrivateKey: string;
googleProjectId: string;
whatsappServiceKey: string;
whatsappBotKey: string;
whatsappServiceNumber: string;
whatsappServiceUrl: string;
facebookWorkplaceVerifyToken: string;
facebookWorkplaceAppSecret: string;
facebookWorkplaceAccessToken: string;
smsKey: string;
smsSecret: string;
smsServiceNumber: string;
theme: string;
ui: string;
kb: string;
nlpAppId: string;
nlpKey: string;
nlpEndpoint: string;
nlpAuthoringKey: string;
deploymentPaths: string;
speechKey: string;
speechEndpoint: string;
spellcheckerKey: string;
spellcheckerEndpoint: string;
searchHost: string;
searchKey: string;
searchIndex: string;
searchIndexer: string;
searchScore: number;
nlpScore: number;
storageUsername: string;
storagePassword: string;
storageName: string;
storageServer: string;
storageDialect: string;
storagePath: string;
adminPass: string;
translatorKey: string;
translatorEndpoint: string;
params: string;
state: string;
}

View file

@ -1,35 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
export interface IGBKBService {
getAnswerTextByMediaName(instanceId: number, answerMediaName: string): Promise<string>;
}

View file

@ -1,82 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
'use strict';
import { IGBCoreService } from './IGBCoreService'
import { Sequelize } from 'sequelize-typescript'
import { GBMinInstance } from '.'
import { GBDialogStep } from './GBDialogStep';
export interface IGBPackage{
/**
* Each app has its own set of sys packages.
*/
sysPackages: IGBPackage[]
/**
* Called when a package is being loaded, once per server or at demand.
*/
loadPackage(core: IGBCoreService, sequelize: Sequelize): Promise<void>
/**
* Called when a package needs to be unloaded.
*/
unloadPackage(core: IGBCoreService): Promise<void>
/**
* Called when a new bot instance is loaded.
*/
getDialogs(min: GBMinInstance)
/**
* Called when a new bot instance is loaded.
*/
loadBot(min: GBMinInstance): Promise<void>
/**
* Called whenever a bot instance needs to be shutdown.
*/
unloadBot(min: GBMinInstance): Promise<void>
/**
* Called in each new session.
*/
onNewSession(min: GBMinInstance, step: GBDialogStep): Promise<void>
/**
* Exchange data between BotServer and .gbapp/.gblib
* @param kind 'newMessage', 'getBroadcast', 'getKeywords'
* @param data
*/
onExchangeData (min: GBMinInstance, kind: string, data: any);
}

324
src/branding.rs Normal file
View file

@ -0,0 +1,324 @@
//! White-Label Branding Module
//!
//! Allows complete customization of platform identity.
//! When a .product file exists with name=MyCustomPlatform,
//! "General Bots" never appears in logs, display, messages, footer - nothing.
use log::info;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::sync::OnceLock;
/// Global branding configuration - loaded once at startup
static BRANDING: OnceLock<BrandingConfig> = OnceLock::new();
/// Default platform name
const DEFAULT_PLATFORM_NAME: &str = "General Bots";
const DEFAULT_PLATFORM_SHORT: &str = "GB";
const DEFAULT_PLATFORM_DOMAIN: &str = "generalbots.com";
/// Branding configuration loaded from .product file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrandingConfig {
/// Platform name (e.g., "MyCustomPlatform")
pub name: String,
/// Short name for logs and compact displays (e.g., "MCP")
pub short_name: String,
/// Company/organization name
pub company: Option<String>,
/// Domain for URLs and emails
pub domain: Option<String>,
/// Support email
pub support_email: Option<String>,
/// Logo URL (for web UI)
pub logo_url: Option<String>,
/// Favicon URL
pub favicon_url: Option<String>,
/// Primary color (hex)
pub primary_color: Option<String>,
/// Secondary color (hex)
pub secondary_color: Option<String>,
/// Footer text
pub footer_text: Option<String>,
/// Copyright text
pub copyright: Option<String>,
/// Custom CSS URL
pub custom_css: Option<String>,
/// Terms of service URL
pub terms_url: Option<String>,
/// Privacy policy URL
pub privacy_url: Option<String>,
/// Documentation URL
pub docs_url: Option<String>,
/// Whether this is a white-label deployment
pub is_white_label: bool,
}
impl Default for BrandingConfig {
fn default() -> Self {
Self {
name: DEFAULT_PLATFORM_NAME.to_string(),
short_name: DEFAULT_PLATFORM_SHORT.to_string(),
company: Some("pragmatismo.com.br".to_string()),
domain: Some(DEFAULT_PLATFORM_DOMAIN.to_string()),
support_email: Some("support@generalbots.com".to_string()),
logo_url: None,
favicon_url: None,
primary_color: Some("#25d366".to_string()),
secondary_color: Some("#075e54".to_string()),
footer_text: None,
copyright: Some(format!(
"© {} pragmatismo.com.br. All rights reserved.",
chrono::Utc::now().format("%Y")
)),
custom_css: None,
terms_url: None,
privacy_url: None,
docs_url: Some("https://docs.generalbots.com".to_string()),
is_white_label: false,
}
}
}
impl BrandingConfig {
/// Load branding from .product file if it exists
pub fn load() -> Self {
let search_paths = [
".product",
"config/.product",
"/etc/botserver/.product",
"/opt/gbo/.product",
];
for path in &search_paths {
if let Ok(config) = Self::load_from_file(path) {
info!("Loaded white-label branding from {}: {}", path, config.name);
return config;
}
}
// Check environment variable
if let Ok(product_file) = std::env::var("PRODUCT_FILE") {
if let Ok(config) = Self::load_from_file(&product_file) {
info!(
"Loaded white-label branding from PRODUCT_FILE={}: {}",
product_file, config.name
);
return config;
}
}
// Check for individual environment overrides
let mut config = Self::default();
if let Ok(name) = std::env::var("PLATFORM_NAME") {
config.name = name;
config.is_white_label = true;
}
if let Ok(short) = std::env::var("PLATFORM_SHORT_NAME") {
config.short_name = short;
}
if let Ok(company) = std::env::var("PLATFORM_COMPANY") {
config.company = Some(company);
}
if let Ok(domain) = std::env::var("PLATFORM_DOMAIN") {
config.domain = Some(domain);
}
if let Ok(logo) = std::env::var("PLATFORM_LOGO_URL") {
config.logo_url = Some(logo);
}
if let Ok(color) = std::env::var("PLATFORM_PRIMARY_COLOR") {
config.primary_color = Some(color);
}
config
}
/// Load from a specific file path
fn load_from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let path = Path::new(path);
if !path.exists() {
return Err("File not found".into());
}
let content = std::fs::read_to_string(path)?;
// Try parsing as TOML first
if let Ok(config) = toml::from_str::<ProductFile>(&content) {
return Ok(config.into());
}
// Try parsing as simple key=value format
let mut config = Self::default();
config.is_white_label = true;
for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
continue;
}
if let Some((key, value)) = line.split_once('=') {
let key = key.trim().to_lowercase();
let value = value.trim().trim_matches('"').trim_matches('\'');
match key.as_str() {
"name" | "platform_name" => config.name = value.to_string(),
"short_name" | "short" => config.short_name = value.to_string(),
"company" | "organization" => config.company = Some(value.to_string()),
"domain" => config.domain = Some(value.to_string()),
"support_email" | "email" => config.support_email = Some(value.to_string()),
"logo_url" | "logo" => config.logo_url = Some(value.to_string()),
"favicon_url" | "favicon" => config.favicon_url = Some(value.to_string()),
"primary_color" | "color" => config.primary_color = Some(value.to_string()),
"secondary_color" => config.secondary_color = Some(value.to_string()),
"footer_text" | "footer" => config.footer_text = Some(value.to_string()),
"copyright" => config.copyright = Some(value.to_string()),
"custom_css" | "css" => config.custom_css = Some(value.to_string()),
"terms_url" | "terms" => config.terms_url = Some(value.to_string()),
"privacy_url" | "privacy" => config.privacy_url = Some(value.to_string()),
"docs_url" | "docs" => config.docs_url = Some(value.to_string()),
_ => {}
}
}
}
Ok(config)
}
}
/// TOML format for .product file
#[derive(Debug, Deserialize)]
struct ProductFile {
name: String,
#[serde(default)]
short_name: Option<String>,
#[serde(default)]
company: Option<String>,
#[serde(default)]
domain: Option<String>,
#[serde(default)]
support_email: Option<String>,
#[serde(default)]
logo_url: Option<String>,
#[serde(default)]
favicon_url: Option<String>,
#[serde(default)]
primary_color: Option<String>,
#[serde(default)]
secondary_color: Option<String>,
#[serde(default)]
footer_text: Option<String>,
#[serde(default)]
copyright: Option<String>,
#[serde(default)]
custom_css: Option<String>,
#[serde(default)]
terms_url: Option<String>,
#[serde(default)]
privacy_url: Option<String>,
#[serde(default)]
docs_url: Option<String>,
}
impl From<ProductFile> for BrandingConfig {
fn from(pf: ProductFile) -> Self {
let short_name = pf.short_name.unwrap_or_else(|| {
pf.name
.split_whitespace()
.map(|w| w.chars().next().unwrap_or('X'))
.collect::<String>()
.to_uppercase()
});
Self {
name: pf.name,
short_name,
company: pf.company,
domain: pf.domain,
support_email: pf.support_email,
logo_url: pf.logo_url,
favicon_url: pf.favicon_url,
primary_color: pf.primary_color,
secondary_color: pf.secondary_color,
footer_text: pf.footer_text,
copyright: pf.copyright,
custom_css: pf.custom_css,
terms_url: pf.terms_url,
privacy_url: pf.privacy_url,
docs_url: pf.docs_url,
is_white_label: true,
}
}
}
// Global Access Functions
/// Initialize branding at application startup
pub fn init_branding() {
let config = BrandingConfig::load();
let _ = BRANDING.set(config);
}
/// Get the current branding configuration
pub fn branding() -> &'static BrandingConfig {
BRANDING.get_or_init(BrandingConfig::load)
}
/// Get the platform name
pub fn platform_name() -> &'static str {
&branding().name
}
/// Get the short platform name
pub fn platform_short() -> &'static str {
&branding().short_name
}
/// Check if this is a white-label deployment
pub fn is_white_label() -> bool {
branding().is_white_label
}
/// Get formatted copyright text
pub fn copyright_text() -> String {
branding().copyright.clone().unwrap_or_else(|| {
format!(
"© {} {}",
chrono::Utc::now().format("%Y"),
branding().company.as_deref().unwrap_or(&branding().name)
)
})
}
/// Get footer text
pub fn footer_text() -> String {
branding()
.footer_text
.clone()
.unwrap_or_else(|| format!("Powered by {}", platform_name()))
}
/// Format a log prefix with platform branding
pub fn log_prefix() -> String {
format!("[{}]", platform_short())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_branding() {
let config = BrandingConfig::default();
assert_eq!(config.name, "General Bots");
assert_eq!(config.short_name, "GB");
assert!(!config.is_white_label);
}
#[test]
fn test_platform_name_function() {
let name = platform_name();
assert!(!name.is_empty());
}
}

227
src/error.rs Normal file
View file

@ -0,0 +1,227 @@
use thiserror::Error;
pub type BotResult<T> = Result<T, BotError>;
#[derive(Error, Debug)]
pub enum BotError {
#[error("Configuration error: {0}")]
Config(String),
#[error("Database error: {0}")]
Database(String),
#[error("HTTP error: {status} - {message}")]
Http { status: u16, message: String },
#[error("Auth error: {0}")]
Auth(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("{entity} not found")]
NotFound { entity: String },
#[error("Conflict: {0}")]
Conflict(String),
#[error("Rate limited: retry after {retry_after_secs}s")]
RateLimited { retry_after_secs: u64 },
#[error("Service unavailable: {0}")]
ServiceUnavailable(String),
#[error("Timeout after {duration_ms}ms")]
Timeout { duration_ms: u64 },
#[error("Internal error: {0}")]
Internal(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("{0}")]
Other(String),
}
impl BotError {
pub fn config(msg: impl Into<String>) -> Self {
Self::Config(msg.into())
}
pub fn database(msg: impl Into<String>) -> Self {
Self::Database(msg.into())
}
pub fn http(status: u16, msg: impl Into<String>) -> Self {
Self::Http {
status,
message: msg.into(),
}
}
pub fn http_msg(msg: impl Into<String>) -> Self {
Self::Http {
status: 500,
message: msg.into(),
}
}
pub fn auth(msg: impl Into<String>) -> Self {
Self::Auth(msg.into())
}
pub fn validation(msg: impl Into<String>) -> Self {
Self::Validation(msg.into())
}
pub fn not_found(entity: impl Into<String>) -> Self {
Self::NotFound {
entity: entity.into(),
}
}
pub fn conflict(msg: impl Into<String>) -> Self {
Self::Conflict(msg.into())
}
pub fn rate_limited(retry_after_secs: u64) -> Self {
Self::RateLimited { retry_after_secs }
}
pub fn service_unavailable(msg: impl Into<String>) -> Self {
Self::ServiceUnavailable(msg.into())
}
pub fn timeout(duration_ms: u64) -> Self {
Self::Timeout { duration_ms }
}
pub fn internal(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
pub fn status_code(&self) -> u16 {
match self {
Self::Config(_) => 500,
Self::Database(_) => 500,
Self::Http { status, .. } => *status,
Self::Auth(_) => 401,
Self::Validation(_) => 400,
Self::NotFound { .. } => 404,
Self::Conflict(_) => 409,
Self::RateLimited { .. } => 429,
Self::ServiceUnavailable(_) => 503,
Self::Timeout { .. } => 504,
Self::Internal(_) => 500,
Self::Io(_) => 500,
Self::Json(_) => 400,
Self::Other(_) => 500,
}
}
pub fn is_retryable(&self) -> bool {
match self {
Self::RateLimited { .. } | Self::ServiceUnavailable(_) | Self::Timeout { .. } => true,
Self::Http { status, .. } => *status >= 500,
_ => false,
}
}
pub fn is_client_error(&self) -> bool {
let code = self.status_code();
(400..500).contains(&code)
}
pub fn is_server_error(&self) -> bool {
self.status_code() >= 500
}
}
impl From<anyhow::Error> for BotError {
fn from(err: anyhow::Error) -> Self {
Self::Other(err.to_string())
}
}
impl From<String> for BotError {
fn from(msg: String) -> Self {
Self::Other(msg)
}
}
impl From<&str> for BotError {
fn from(msg: &str) -> Self {
Self::Other(msg.to_string())
}
}
#[cfg(feature = "http-client")]
impl From<reqwest::Error> for BotError {
fn from(err: reqwest::Error) -> Self {
let status = err.status().map(|s| s.as_u16()).unwrap_or(500);
Self::Http {
status,
message: err.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = BotError::config("missing API key");
assert_eq!(err.to_string(), "Configuration error: missing API key");
}
#[test]
fn test_not_found_error() {
let err = BotError::not_found("User");
assert_eq!(err.to_string(), "User not found");
assert_eq!(err.status_code(), 404);
}
#[test]
fn test_http_error_with_status() {
let err = BotError::http(503, "Service down");
assert_eq!(err.status_code(), 503);
assert!(err.is_server_error());
assert!(!err.is_client_error());
}
#[test]
fn test_validation_error() {
let err = BotError::validation("Invalid email format");
assert_eq!(err.status_code(), 400);
assert!(err.is_client_error());
}
#[test]
fn test_retryable_errors() {
assert!(BotError::rate_limited(60).is_retryable());
assert!(BotError::service_unavailable("down").is_retryable());
assert!(BotError::timeout(5000).is_retryable());
assert!(!BotError::validation("bad input").is_retryable());
assert!(!BotError::not_found("User").is_retryable());
}
#[test]
fn test_rate_limited_display() {
let err = BotError::rate_limited(30);
assert_eq!(err.to_string(), "Rate limited: retry after 30s");
assert_eq!(err.status_code(), 429);
}
#[test]
fn test_timeout_display() {
let err = BotError::timeout(5000);
assert_eq!(err.to_string(), "Timeout after 5000ms");
assert_eq!(err.status_code(), 504);
}
}

219
src/http_client.rs Normal file
View file

@ -0,0 +1,219 @@
use crate::error::BotError;
use log::{debug, error};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;
use std::time::Duration;
const DEFAULT_BOTSERVER_URL: &str = "https://localhost:8088";
const DEFAULT_TIMEOUT_SECS: u64 = 30;
#[derive(Clone)]
pub struct BotServerClient {
client: Arc<reqwest::Client>,
base_url: String,
}
impl BotServerClient {
pub fn new(base_url: Option<String>) -> Self {
Self::with_timeout(base_url, Duration::from_secs(DEFAULT_TIMEOUT_SECS))
}
pub fn with_timeout(base_url: Option<String>, timeout: Duration) -> Self {
let url = base_url.unwrap_or_else(|| {
std::env::var("BOTSERVER_URL").unwrap_or_else(|_| DEFAULT_BOTSERVER_URL.to_string())
});
let client = reqwest::Client::builder()
.timeout(timeout)
.user_agent(format!("BotLib/{}", env!("CARGO_PKG_VERSION")))
.danger_accept_invalid_certs(true)
.build()
.expect("Failed to create HTTP client");
Self {
client: Arc::new(client),
base_url: url,
}
}
pub fn base_url(&self) -> &str {
&self.base_url
}
pub async fn get<T: DeserializeOwned>(&self, endpoint: &str) -> Result<T, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("GET {}", url);
let response = self.client.get(&url).send().await?;
self.handle_response(response).await
}
pub async fn post<T: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: &T,
) -> Result<R, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("POST {}", url);
let response = self.client.post(&url).json(body).send().await?;
self.handle_response(response).await
}
pub async fn put<T: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: &T,
) -> Result<R, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("PUT {}", url);
let response = self.client.put(&url).json(body).send().await?;
self.handle_response(response).await
}
pub async fn patch<T: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: &T,
) -> Result<R, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("PATCH {}", url);
let response = self.client.patch(&url).json(body).send().await?;
self.handle_response(response).await
}
pub async fn delete<T: DeserializeOwned>(&self, endpoint: &str) -> Result<T, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("DELETE {}", url);
let response = self.client.delete(&url).send().await?;
self.handle_response(response).await
}
pub async fn get_authorized<T: DeserializeOwned>(
&self,
endpoint: &str,
token: &str,
) -> Result<T, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("GET {} (authorized)", url);
let response = self.client.get(&url).bearer_auth(token).send().await?;
self.handle_response(response).await
}
pub async fn post_authorized<T: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: &T,
token: &str,
) -> Result<R, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("POST {} (authorized)", url);
let response = self
.client
.post(&url)
.bearer_auth(token)
.json(body)
.send()
.await?;
self.handle_response(response).await
}
pub async fn delete_authorized<T: DeserializeOwned>(
&self,
endpoint: &str,
token: &str,
) -> Result<T, BotError> {
let url = format!("{}{}", self.base_url, endpoint);
debug!("DELETE {} (authorized)", url);
let response = self.client.delete(&url).bearer_auth(token).send().await?;
self.handle_response(response).await
}
pub async fn health_check(&self) -> bool {
match self.get::<serde_json::Value>("/health").await {
Ok(_) => true,
Err(e) => {
error!("Health check failed: {}", e);
false
}
}
}
async fn handle_response<T: DeserializeOwned>(
&self,
response: reqwest::Response,
) -> Result<T, BotError> {
let status = response.status();
let status_code = status.as_u16();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|_| "Unknown error".to_string());
error!("HTTP {} error: {}", status_code, error_text);
return Err(BotError::http(status_code, error_text));
}
response.json().await.map_err(|e| {
error!("Failed to parse response: {}", e);
BotError::http(status_code, format!("Failed to parse response: {}", e))
})
}
}
impl std::fmt::Debug for BotServerClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BotServerClient")
.field("base_url", &self.base_url)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_creation() {
let client = BotServerClient::new(Some("http://localhost:8080".to_string()));
assert_eq!(client.base_url(), "http://localhost:8080");
}
#[test]
fn test_client_default_url() {
std::env::remove_var("BOTSERVER_URL");
let client = BotServerClient::new(None);
assert_eq!(client.base_url(), DEFAULT_BOTSERVER_URL);
}
#[test]
fn test_client_with_timeout() {
let client = BotServerClient::with_timeout(
Some("http://test:9000".to_string()),
Duration::from_secs(60),
);
assert_eq!(client.base_url(), "http://test:9000");
}
#[test]
fn test_client_with_timeout_default_url() {
std::env::remove_var("BOTSERVER_URL");
let client = BotServerClient::with_timeout(None, Duration::from_secs(60));
assert_eq!(client.base_url(), DEFAULT_BOTSERVER_URL);
}
#[test]
fn test_client_debug() {
let client = BotServerClient::new(Some("http://debug-test".to_string()));
let debug_str = format!("{:?}", client);
assert!(debug_str.contains("BotServerClient"));
assert!(debug_str.contains("http://debug-test"));
}
}

View file

@ -1,47 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
"use strict";
export { Sequelize } from "sequelize-typescript";
export { IGBConversationalService } from "./IGBConversationalService";
export { IGBKBService } from "./IGBKBService";
export { IGBCoreService } from "./IGBCoreService";
export { IGBDialog } from "./IGBDialog";
export { IGBPackage } from "./IGBPackage";
export { IGBInstance } from "./IGBInstance";
export { GBError, GBERROR_TYPE } from "./GBError";
export { GBService } from "./GBService";
export { GBMinInstance } from "./GBMinInstance";
export { IGBAdminService } from "./IGBAdminService";
export { IGBInstallationDeployer } from "./IGBInstallationDeployer";
export { IGBDeployer } from "./IGBDeployer";
export { GBDialogStep } from "./GBDialogStep";
export { GBLog } from "./GBLog";

30
src/lib.rs Normal file
View file

@ -0,0 +1,30 @@
//! BotLib - Shared library for General Bots
//!
//! This crate provides common types, utilities, and abstractions
//! shared between botserver and botui.
//!
//! # Features
//! - `database` - Database connection utilities (diesel)
//! - `http-client` - HTTP client for API calls
//! - `validation` - Request validation derive macros
pub mod branding;
pub mod error;
#[cfg(feature = "http-client")]
pub mod http_client;
pub mod message_types;
pub mod models;
pub mod version;
// Re-exports for convenience
pub use branding::{branding, init_branding, is_white_label, platform_name, platform_short, BrandingConfig};
pub use error::{BotError, BotResult};
pub use message_types::MessageType;
pub use models::{ApiResponse, BotResponse, Session, Suggestion, UserMessage};
pub use version::{
get_botserver_version, init_version_registry, register_component, version_string,
ComponentSource, ComponentStatus, ComponentVersion, VersionRegistry, BOTSERVER_VERSION,
};
#[cfg(feature = "http-client")]
pub use http_client::BotServerClient;

View file

@ -1,74 +0,0 @@
/*****************************************************************************\
| ® |
| |
| |
| |
| |
| |
| General Bots Copyright (c) pragmatismo.cloud. All rights reserved. |
| Licensed under the AGPL-3.0. |
| |
| According to our dual licensing model, this program can be used either |
| under the terms of the GNU Affero General Public License, version 3, |
| or under a proprietary license. |
| |
| The texts of the GNU Affero General Public License with an additional |
| permission and of our proprietary license can be found at and |
| in the LICENSE file you have received along with this program. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY, without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| "General Bots" is a registered trademark of pragmatismo.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. |
| |
\*****************************************************************************/
/**
* @fileoverview Logging support.
*/
const { createLogger, format, transports } = require('winston');
const config = {
levels: {
error: 0,
debug: 1,
warn: 2,
data: 3,
info: 4,
verbose: 5,
silly: 6,
custom: 7
},
colors: {
error: 'red',
debug: 'blue',
warn: 'yellow',
data: 'grey',
info: 'green',
verbose: 'cyan',
silly: 'magenta',
custom: 'yellow'
}
};
const logger = createLogger({
format: format.combine(
format.colorize(),
format.simple(),
format.label({ label: 'GB' }),
format.timestamp(),
format.printf(nfo => {
return `${nfo.timestamp.replace(/\-|\.|\d\d\dZ|\:/gi, '')} ${nfo.label} ${nfo.level} ${nfo.message}`;
})
),
levels: config.levels,
transports: [new transports.Console()]
});
module.exports = [logger];

86
src/message_types.rs Normal file
View file

@ -0,0 +1,86 @@
//! Message type definitions
//!
//! Defines the different types of messages in the bot system.
use serde::{Deserialize, Serialize};
/// Enum representing different types of messages in the bot system
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct MessageType(pub i32);
impl MessageType {
/// Regular message from external systems (WhatsApp, Instagram, etc.)
pub const EXTERNAL: MessageType = MessageType(0);
/// User message from web interface
pub const USER: MessageType = MessageType(1);
/// Bot response (can be regular content or event)
pub const BOT_RESPONSE: MessageType = MessageType(2);
/// Continue interrupted response
pub const CONTINUE: MessageType = MessageType(3);
/// Suggestion or command message
pub const SUGGESTION: MessageType = MessageType(4);
/// Context change notification
pub const CONTEXT_CHANGE: MessageType = MessageType(5);
}
impl From<i32> for MessageType {
fn from(value: i32) -> Self {
MessageType(value)
}
}
impl From<MessageType> for i32 {
fn from(value: MessageType) -> Self {
value.0
}
}
impl Default for MessageType {
fn default() -> Self {
MessageType::USER
}
}
impl std::fmt::Display for MessageType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self.0 {
0 => "EXTERNAL",
1 => "USER",
2 => "BOT_RESPONSE",
3 => "CONTINUE",
4 => "SUGGESTION",
5 => "CONTEXT_CHANGE",
_ => "UNKNOWN",
};
write!(f, "{}", name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_type_conversion() {
assert_eq!(i32::from(MessageType::USER), 1);
assert_eq!(MessageType::from(2), MessageType::BOT_RESPONSE);
}
#[test]
fn test_message_type_display() {
assert_eq!(MessageType::USER.to_string(), "USER");
assert_eq!(MessageType::BOT_RESPONSE.to_string(), "BOT_RESPONSE");
}
#[test]
fn test_message_type_equality() {
assert_eq!(MessageType::USER, MessageType(1));
assert_ne!(MessageType::USER, MessageType::BOT_RESPONSE);
}
}

540
src/models.rs Normal file
View file

@ -0,0 +1,540 @@
use crate::message_types::MessageType;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResponse<T> {
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
}
impl<T> ApiResponse<T> {
pub fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
message: None,
code: None,
}
}
pub fn success_with_message(data: T, message: impl Into<String>) -> Self {
Self {
success: true,
data: Some(data),
error: None,
message: Some(message.into()),
code: None,
}
}
pub fn error(message: impl Into<String>) -> Self {
Self {
success: false,
data: None,
error: Some(message.into()),
message: None,
code: None,
}
}
pub fn error_with_code(message: impl Into<String>, code: impl Into<String>) -> Self {
Self {
success: false,
data: None,
error: Some(message.into()),
message: None,
code: Some(code.into()),
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> ApiResponse<U> {
ApiResponse {
success: self.success,
data: self.data.map(f),
error: self.error,
message: self.message,
code: self.code,
}
}
pub fn is_success(&self) -> bool {
self.success
}
pub fn is_error(&self) -> bool {
!self.success
}
}
impl<T: Default> Default for ApiResponse<T> {
fn default() -> Self {
Self::success(T::default())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub id: Uuid,
pub user_id: Uuid,
pub bot_id: Uuid,
pub title: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<DateTime<Utc>>,
}
impl Session {
pub fn new(user_id: Uuid, bot_id: Uuid, title: impl Into<String>) -> Self {
let now = Utc::now();
Self {
id: Uuid::new_v4(),
user_id,
bot_id,
title: title.into(),
created_at: now,
updated_at: now,
expires_at: None,
}
}
pub fn with_expiry(mut self, expires_at: DateTime<Utc>) -> Self {
self.expires_at = Some(expires_at);
self
}
pub fn is_expired(&self) -> bool {
self.expires_at.map(|exp| Utc::now() > exp).unwrap_or(false)
}
pub fn is_active(&self) -> bool {
!self.is_expired()
}
pub fn remaining_time(&self) -> Option<chrono::Duration> {
self.expires_at.map(|exp| exp - Utc::now())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMessage {
pub bot_id: String,
pub user_id: String,
pub session_id: String,
pub channel: String,
pub content: String,
pub message_type: MessageType,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_url: Option<String>,
pub timestamp: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_name: Option<String>,
}
impl UserMessage {
pub fn text(
bot_id: impl Into<String>,
user_id: impl Into<String>,
session_id: impl Into<String>,
channel: impl Into<String>,
content: impl Into<String>,
) -> Self {
Self {
bot_id: bot_id.into(),
user_id: user_id.into(),
session_id: session_id.into(),
channel: channel.into(),
content: content.into(),
message_type: MessageType::USER,
media_url: None,
timestamp: Utc::now(),
context_name: None,
}
}
pub fn with_media(mut self, url: impl Into<String>) -> Self {
self.media_url = Some(url.into());
self
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context_name = Some(context.into());
self
}
pub fn has_media(&self) -> bool {
self.media_url.is_some()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Suggestion {
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icon: Option<String>,
}
impl Suggestion {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
context: None,
action: None,
icon: None,
}
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
pub fn with_action(mut self, action: impl Into<String>) -> Self {
self.action = Some(action.into());
self
}
pub fn with_icon(mut self, icon: impl Into<String>) -> Self {
self.icon = Some(icon.into());
self
}
}
impl<S: Into<String>> From<S> for Suggestion {
fn from(text: S) -> Self {
Self::new(text)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BotResponse {
pub bot_id: String,
pub user_id: String,
pub session_id: String,
pub channel: String,
pub content: String,
pub message_type: MessageType,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_token: Option<String>,
pub is_complete: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub suggestions: Vec<Suggestion>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_name: Option<String>,
#[serde(default)]
pub context_length: usize,
#[serde(default)]
pub context_max_length: usize,
}
impl BotResponse {
pub fn new(
bot_id: impl Into<String>,
session_id: impl Into<String>,
user_id: impl Into<String>,
content: impl Into<String>,
channel: impl Into<String>,
) -> Self {
Self {
bot_id: bot_id.into(),
user_id: user_id.into(),
session_id: session_id.into(),
channel: channel.into(),
content: content.into(),
message_type: MessageType::BOT_RESPONSE,
stream_token: None,
is_complete: true,
suggestions: Vec::new(),
context_name: None,
context_length: 0,
context_max_length: 0,
}
}
pub fn streaming(
bot_id: impl Into<String>,
session_id: impl Into<String>,
user_id: impl Into<String>,
channel: impl Into<String>,
stream_token: impl Into<String>,
) -> Self {
Self {
bot_id: bot_id.into(),
user_id: user_id.into(),
session_id: session_id.into(),
channel: channel.into(),
content: String::new(),
message_type: MessageType::BOT_RESPONSE,
stream_token: Some(stream_token.into()),
is_complete: false,
suggestions: Vec::new(),
context_name: None,
context_length: 0,
context_max_length: 0,
}
}
pub fn with_suggestions<I, S>(mut self, suggestions: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<Suggestion>,
{
self.suggestions = suggestions.into_iter().map(Into::into).collect();
self
}
pub fn add_suggestion(mut self, suggestion: impl Into<Suggestion>) -> Self {
self.suggestions.push(suggestion.into());
self
}
pub fn with_context(
mut self,
name: impl Into<String>,
length: usize,
max_length: usize,
) -> Self {
self.context_name = Some(name.into());
self.context_length = length;
self.context_max_length = max_length;
self
}
pub fn append_content(&mut self, chunk: &str) {
self.content.push_str(chunk);
}
pub fn complete(mut self) -> Self {
self.is_complete = true;
self
}
pub fn is_streaming(&self) -> bool {
self.stream_token.is_some() && !self.is_complete
}
pub fn has_suggestions(&self) -> bool {
!self.suggestions.is_empty()
}
}
impl Default for BotResponse {
fn default() -> Self {
Self {
bot_id: String::new(),
user_id: String::new(),
session_id: String::new(),
channel: String::new(),
content: String::new(),
message_type: MessageType::BOT_RESPONSE,
stream_token: None,
is_complete: true,
suggestions: Vec::new(),
context_name: None,
context_length: 0,
context_max_length: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
pub attachment_type: AttachmentType,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_url: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AttachmentType {
Image,
Audio,
Video,
Document,
File,
}
impl Attachment {
pub fn new(attachment_type: AttachmentType, url: impl Into<String>) -> Self {
Self {
attachment_type,
url: url.into(),
mime_type: None,
filename: None,
size: None,
thumbnail_url: None,
}
}
pub fn image(url: impl Into<String>) -> Self {
Self::new(AttachmentType::Image, url)
}
pub fn audio(url: impl Into<String>) -> Self {
Self::new(AttachmentType::Audio, url)
}
pub fn video(url: impl Into<String>) -> Self {
Self::new(AttachmentType::Video, url)
}
pub fn document(url: impl Into<String>) -> Self {
Self::new(AttachmentType::Document, url)
}
pub fn file(url: impl Into<String>) -> Self {
Self::new(AttachmentType::File, url)
}
pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
self.mime_type = Some(mime_type.into());
self
}
pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
self.filename = Some(filename.into());
self
}
pub fn with_size(mut self, size: u64) -> Self {
self.size = Some(size);
self
}
pub fn with_thumbnail(mut self, thumbnail_url: impl Into<String>) -> Self {
self.thumbnail_url = Some(thumbnail_url.into());
self
}
pub fn is_image(&self) -> bool {
self.attachment_type == AttachmentType::Image
}
pub fn is_media(&self) -> bool {
matches!(
self.attachment_type,
AttachmentType::Image | AttachmentType::Audio | AttachmentType::Video
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_response_success() {
let response: ApiResponse<String> = ApiResponse::success("test".to_string());
assert!(response.is_success());
assert!(!response.is_error());
assert_eq!(response.data, Some("test".to_string()));
assert!(response.error.is_none());
}
#[test]
fn test_api_response_error() {
let response: ApiResponse<String> = ApiResponse::error("something went wrong");
assert!(!response.is_success());
assert!(response.is_error());
assert!(response.data.is_none());
assert_eq!(response.error, Some("something went wrong".to_string()));
}
#[test]
fn test_api_response_map() {
let response: ApiResponse<i32> = ApiResponse::success(42);
let mapped = response.map(|n| n.to_string());
assert_eq!(mapped.data, Some("42".to_string()));
}
#[test]
fn test_session_creation() {
let user_id = Uuid::new_v4();
let bot_id = Uuid::new_v4();
let session = Session::new(user_id, bot_id, "Test Session");
assert_eq!(session.user_id, user_id);
assert_eq!(session.bot_id, bot_id);
assert_eq!(session.title, "Test Session");
assert!(session.is_active());
assert!(!session.is_expired());
}
#[test]
fn test_user_message_creation() {
let msg =
UserMessage::text("bot1", "user1", "sess1", "web", "Hello!").with_context("greeting");
assert_eq!(msg.content, "Hello!");
assert_eq!(msg.message_type, MessageType::USER);
assert_eq!(msg.context_name, Some("greeting".to_string()));
}
#[test]
fn test_bot_response_creation() {
let response = BotResponse::new("bot1", "sess1", "user1", "Hi there!", "web")
.add_suggestion("Option 1")
.add_suggestion("Option 2");
assert!(response.is_complete);
assert!(!response.is_streaming());
assert!(response.has_suggestions());
assert_eq!(response.suggestions.len(), 2);
}
#[test]
fn test_bot_response_streaming() {
let mut response = BotResponse::streaming("bot1", "sess1", "user1", "web", "token123");
assert!(response.is_streaming());
assert!(!response.is_complete);
response.append_content("Hello ");
response.append_content("World!");
assert_eq!(response.content, "Hello World!");
let response = response.complete();
assert!(!response.is_streaming());
assert!(response.is_complete);
}
#[test]
fn test_attachment_creation() {
let attachment = Attachment::image("https://example.com/photo.jpg")
.with_filename("photo.jpg")
.with_size(1024)
.with_mime_type("image/jpeg");
assert!(attachment.is_image());
assert!(attachment.is_media());
assert_eq!(attachment.filename, Some("photo.jpg".to_string()));
assert_eq!(attachment.size, Some(1024));
}
#[test]
fn test_suggestion_from_string() {
let suggestion: Suggestion = "Click here".into();
assert_eq!(suggestion.text, "Click here");
assert!(suggestion.context.is_none());
}
}

201
src/resilience.rs Normal file
View file

@ -0,0 +1,201 @@
//! Resilience Module - Production-grade fault tolerance primitives
//!
//! This module provides battle-tested resilience patterns:
//! - Retry with exponential backoff and jitter
//! - Circuit breaker with half-open state
//! - Timeout wrappers
//! - Bulkhead isolation
//! - Fallback chains
//!
//! # Design Principles
//! - Zero-cost abstractions where possible
//! - No panics - all errors are recoverable
//! - Composable patterns
//! - Observable state for metrics
use std::future::Future;
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::{RwLock, Semaphore, SemaphorePermit};
use tokio::time::{sleep, timeout};
/// Errors that can occur during resilient operations
#[derive(Debug, Clone)]
pub enum ResilienceError {
/// Operation timed out
Timeout { duration: Duration },
/// Circuit breaker is open, rejecting requests
CircuitOpen { until: Option<Duration> },
/// All retry attempts exhausted
RetriesExhausted {
attempts: u32,
last_error: String,
},
/// Bulkhead rejected request (too many concurrent)
BulkheadFull { max_concurrent: usize },
/// Wrapped error from the underlying operation
Operation(String),
}
impl std::fmt::Display for ResilienceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Timeout { duration } => {
write!(f, "Operation timed out after {:?}", duration)
}
Self::CircuitOpen { until } => {
if let Some(d) = until {
write!(f, "Circuit breaker open, retry after {:?}", d)
} else {
write!(f, "Circuit breaker open")
}
}
Self::RetriesExhausted {
attempts,
last_error,
} => {
write!(
f,
"All {} retry attempts exhausted. Last error: {}",
attempts, last_error
)
}
Self::BulkheadFull { max_concurrent } => {
write!(
f,
"Bulkhead full, max {} concurrent requests",
max_concurrent
)
}
Self::Operation(msg) => write!(f, "Operation failed: {}", msg),
}
}
}
impl std::error::Error for ResilienceError {}
// ============================================================================
// Retry Configuration and Execution
// ============================================================================
/// Retry strategy configuration
#[derive(Debug, Clone)]
pub struct RetryConfig {
/// Maximum number of attempts (including the first one)
pub max_attempts: u32,
/// Initial delay between retries
pub initial_delay: Duration,
/// Maximum delay between retries
pub max_delay: Duration,
/// Multiplier for exponential backoff (typically 2.0)
pub backoff_multiplier: f64,
/// Add random jitter to prevent thundering herd (0.0 to 1.0)
pub jitter_factor: f64,
/// Predicate to determine if error is retryable
retryable: Option<Arc<dyn Fn(&str) -> bool + Send + Sync>>,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_attempts: 3,
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
backoff_multiplier: 2.0,
jitter_factor: 0.2,
retryable: None,
}
}
}
impl RetryConfig {
/// Create a new retry config with custom max attempts
pub fn with_max_attempts(mut self, attempts: u32) -> Self {
self.max_attempts = attempts.max(1);
self
}
/// Set initial delay
pub fn with_initial_delay(mut self, delay: Duration) -> Self {
self.initial_delay = delay;
self
}
/// Set maximum delay cap
pub fn with_max_delay(mut self, delay: Duration) -> Self {
self.max_delay = delay;
self
}
/// Set backoff multiplier
pub fn with_backoff_multiplier(mut self, multiplier: f64) -> Self {
self.backoff_multiplier = multiplier.max(1.0);
self
}
/// Set jitter factor (0.0 to 1.0)
pub fn with_jitter(mut self, jitter: f64) -> Self {
self.jitter_factor = jitter.clamp(0.0, 1.0);
self
}
/// Set custom retryable predicate
pub fn with_retryable<F>(mut self, predicate: F) -> Self
where
F: Fn(&str) -> bool + Send + Sync + 'static,
{
self.retryable = Some(Arc::new(predicate));
self
}
/// Aggressive retry for critical operations
pub fn aggressive() -> Self {
Self {
max_attempts: 5,
initial_delay: Duration::from_millis(50),
max_delay: Duration::from_secs(10),
backoff_multiplier: 1.5,
jitter_factor: 0.3,
retryable: None,
}
}
/// Conservative retry for non-critical operations
pub fn conservative() -> Self {
Self {
max_attempts: 2,
initial_delay: Duration::from_millis(500),
max_delay: Duration::from_secs(5),
backoff_multiplier: 2.0,
jitter_factor: 0.1,
retryable: None,
}
}
/// Calculate delay for a given attempt number
fn calculate_delay(&self, attempt: u32) -> Duration {
let base_delay = self.initial_delay.as_secs_f64()
* self.backoff_multiplier.powi(attempt.saturating_sub(1) as i32);
let capped_delay = base_delay.min(self.max_delay.as_secs_f64());
// Add jitter
let jitter = if self.jitter_factor > 0.0 {
let jitter_range = capped_delay * self.jitter_factor;
// Simple deterministic "random" based on attempt number
let pseudo_random = ((attempt as f64 * 1.618033988749895) % 1.0) * 2.0 - 1.0;
jitter_range * pseudo_random
} else {
0.0
};
Duration::from_secs_f64((capped_delay + jitter).max(0.001))
}
/// Check if an error is retryable
fn is_retryable(&self, error: &str) -> bool {
if let Some(ref predicate) = self.retryable {
predicate(error)
} else {
//

346
src/version.rs Normal file
View file

@ -0,0 +1,346 @@
//! Version Tracking Module
//!
//! Tracks versions of all components and checks for updates.
use chrono::{DateTime, Utc};
use log::debug;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::RwLock;
/// Global version registry
static VERSION_REGISTRY: RwLock<Option<VersionRegistry>> = RwLock::new(None);
/// Current botserver version from Cargo.toml
pub const BOTSERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const BOTSERVER_NAME: &str = env!("CARGO_PKG_NAME");
/// Component version information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentVersion {
/// Component name
pub name: String,
/// Current installed version
pub version: String,
/// Latest available version (if known)
pub latest_version: Option<String>,
/// Whether an update is available
pub update_available: bool,
/// Component status
pub status: ComponentStatus,
/// Last check time
pub last_checked: Option<DateTime<Utc>>,
/// Source/origin of the component
pub source: ComponentSource,
/// Additional metadata
pub metadata: HashMap<String, String>,
}
/// Component status
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ComponentStatus {
Running,
Stopped,
Error,
Updating,
NotInstalled,
Unknown,
}
impl std::fmt::Display for ComponentStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentStatus::Running => write!(f, "[OK] Running"),
ComponentStatus::Stopped => write!(f, "[STOP] Stopped"),
ComponentStatus::Error => write!(f, "[ERR] Error"),
ComponentStatus::Updating => write!(f, "[UPD] Updating"),
ComponentStatus::NotInstalled => write!(f, "[--] Not Installed"),
ComponentStatus::Unknown => write!(f, "[?] Unknown"),
}
}
}
/// Component source type
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ComponentSource {
Builtin,
Docker,
Lxc,
System,
Binary,
External,
}
impl std::fmt::Display for ComponentSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentSource::Builtin => write!(f, "Built-in"),
ComponentSource::Docker => write!(f, "Docker"),
ComponentSource::Lxc => write!(f, "LXC"),
ComponentSource::System => write!(f, "System"),
ComponentSource::Binary => write!(f, "Binary"),
ComponentSource::External => write!(f, "External"),
}
}
}
/// Version registry holding all component versions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionRegistry {
pub core_version: String,
pub components: HashMap<String, ComponentVersion>,
pub last_update_check: Option<DateTime<Utc>>,
pub update_url: Option<String>,
}
impl Default for VersionRegistry {
fn default() -> Self {
Self {
core_version: BOTSERVER_VERSION.to_string(),
components: HashMap::new(),
last_update_check: None,
update_url: Some("https://api.generalbots.com/updates".to_string()),
}
}
}
impl VersionRegistry {
/// Create a new version registry
pub fn new() -> Self {
let mut registry = Self::default();
registry.register_builtin_components();
registry
}
/// Register built-in components
fn register_builtin_components(&mut self) {
self.register_component(ComponentVersion {
name: "botserver".to_string(),
version: BOTSERVER_VERSION.to_string(),
latest_version: None,
update_available: false,
status: ComponentStatus::Running,
last_checked: Some(Utc::now()),
source: ComponentSource::Builtin,
metadata: HashMap::from([
("description".to_string(), "Core bot server".to_string()),
(
"repo".to_string(),
"https://github.com/GeneralBots/botserver".to_string(),
),
]),
});
self.register_component(ComponentVersion {
name: "basic".to_string(),
version: BOTSERVER_VERSION.to_string(),
latest_version: None,
update_available: false,
status: ComponentStatus::Running,
last_checked: Some(Utc::now()),
source: ComponentSource::Builtin,
metadata: HashMap::from([(
"description".to_string(),
"BASIC script interpreter".to_string(),
)]),
});
self.register_component(ComponentVersion {
name: "llm".to_string(),
version: BOTSERVER_VERSION.to_string(),
latest_version: None,
update_available: false,
status: ComponentStatus::Running,
last_checked: Some(Utc::now()),
source: ComponentSource::Builtin,
metadata: HashMap::from([(
"description".to_string(),
"LLM integration (Claude, GPT, etc.)".to_string(),
)]),
});
}
/// Register a component
pub fn register_component(&mut self, component: ComponentVersion) {
debug!(
"Registered component: {} v{}",
component.name, component.version
);
self.components.insert(component.name.clone(), component);
}
/// Update component status
pub fn update_status(&mut self, name: &str, status: ComponentStatus) {
if let Some(component) = self.components.get_mut(name) {
component.status = status;
}
}
/// Update component version
pub fn update_version(&mut self, name: &str, version: String) {
if let Some(component) = self.components.get_mut(name) {
component.version = version;
component.last_checked = Some(Utc::now());
}
}
/// Get component by name
pub fn get_component(&self, name: &str) -> Option<&ComponentVersion> {
self.components.get(name)
}
/// Get all components
pub fn get_all_components(&self) -> &HashMap<String, ComponentVersion> {
&self.components
}
/// Get components with available updates
pub fn get_available_updates(&self) -> Vec<&ComponentVersion> {
self.components
.values()
.filter(|c| c.update_available)
.collect()
}
/// Get summary of all components
pub fn summary(&self) -> String {
let running = self
.components
.values()
.filter(|c| c.status == ComponentStatus::Running)
.count();
let total = self.components.len();
let updates = self.get_available_updates().len();
format!(
"{} v{} | {}/{} components running | {} updates available",
BOTSERVER_NAME, self.core_version, running, total, updates
)
}
/// Get summary as JSON
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
}
// Global Access Functions
/// Initialize version registry at startup
pub fn init_version_registry() {
let registry = VersionRegistry::new();
if let Ok(mut guard) = VERSION_REGISTRY.write() {
*guard = Some(registry);
}
}
/// Get version registry (read-only)
pub fn version_registry() -> Option<VersionRegistry> {
VERSION_REGISTRY.read().ok()?.clone()
}
/// Get mutable version registry
pub fn version_registry_mut(
) -> Option<std::sync::RwLockWriteGuard<'static, Option<VersionRegistry>>> {
VERSION_REGISTRY.write().ok()
}
/// Register a component
pub fn register_component(component: ComponentVersion) {
if let Ok(mut guard) = VERSION_REGISTRY.write() {
if let Some(ref mut registry) = *guard {
registry.register_component(component);
}
}
}
/// Update component status
pub fn update_component_status(name: &str, status: ComponentStatus) {
if let Ok(mut guard) = VERSION_REGISTRY.write() {
if let Some(ref mut registry) = *guard {
registry.update_status(name, status);
}
}
}
/// Get component version
pub fn get_component_version(name: &str) -> Option<ComponentVersion> {
VERSION_REGISTRY
.read()
.ok()?
.as_ref()?
.get_component(name)
.cloned()
}
/// Get botserver version
pub fn get_botserver_version() -> &'static str {
BOTSERVER_VERSION
}
/// Get version string for display
pub fn version_string() -> String {
format!("{} v{}", BOTSERVER_NAME, BOTSERVER_VERSION)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry_creation() {
let registry = VersionRegistry::new();
assert!(!registry.core_version.is_empty());
assert!(registry.components.contains_key("botserver"));
}
#[test]
fn test_component_registration() {
let mut registry = VersionRegistry::new();
registry.register_component(ComponentVersion {
name: "test".to_string(),
version: "1.0.0".to_string(),
latest_version: None,
update_available: false,
status: ComponentStatus::Running,
last_checked: None,
source: ComponentSource::Builtin,
metadata: HashMap::new(),
});
assert!(registry.get_component("test").is_some());
}
#[test]
fn test_status_display() {
assert_eq!(ComponentStatus::Running.to_string(), "[OK] Running");
assert_eq!(ComponentStatus::Error.to_string(), "[ERR] Error");
}
#[test]
fn test_version_string() {
let vs = version_string();
assert!(!vs.is_empty());
assert!(vs.contains('v'));
}
#[test]
fn test_source_display() {
assert_eq!(ComponentSource::Builtin.to_string(), "Built-in");
assert_eq!(ComponentSource::Docker.to_string(), "Docker");
}
#[test]
fn test_update_status() {
let mut registry = VersionRegistry::new();
registry.update_status("botserver", ComponentStatus::Stopped);
let component = registry.get_component("botserver").unwrap();
assert_eq!(component.status, ComponentStatus::Stopped);
}
#[test]
fn test_summary() {
let registry = VersionRegistry::new();
let summary = registry.summary();
assert!(summary.contains("components running"));
}
}

View file

@ -1,20 +0,0 @@
{
"compilerOptions": {
"allowJs": false,
"baseUrl": "./",
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"mapRoot": "./dist/",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist",
"paths": {},
"sourceMap": true,
"target": "es6",
"typeRoots": ["node_modules/@types"]
},
"include": ["test/**/*", "src/**/*"],
"exclude": ["dist", "node_modules"]
}

View file

@ -1,16 +0,0 @@
{
"mode": "modules",
"out": "docs",
"name": "General Bots® Open Core",
"theme": "default",
"ignoreCompilerErrors": "true",
"experimentalDecorators": "true",
"emitDecoratorMetadata": "true",
"target": "ES6",
"moduleResolution": "node",
"preserveConstEnums": "true",
"stripInternal": "true",
"suppressExcessPropertyErrors": "true",
"suppressImplicitAnyIndexErrors": "true",
"module": "commonjs"
}