Add templates from botserver
This commit is contained in:
parent
280826f53a
commit
186cacddd6
207 changed files with 654287 additions and 287 deletions
494
README.md
494
README.md
|
|
@ -1,353 +1,273 @@
|
|||
# General Bots Templates
|
||||
|
||||
Pre-built templates for bots, apps, prompts, and UI components. Everything you need to get started quickly.
|
||||
Pre-built bot packages for common business use cases. Templates are organized by category for easy discovery.
|
||||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
## Complete Template List (Flat Reference)
|
||||
|
||||
```
|
||||
bottemplates/
|
||||
├── bots/ # Complete bot packages (.gbai)
|
||||
│ ├── core/ # Essential starter bots
|
||||
│ ├── sales/ # CRM, marketing, lead management
|
||||
│ ├── compliance/ # LGPD, GDPR, HIPAA
|
||||
│ ├── finance/ # Banking, payments, invoicing
|
||||
│ ├── healthcare/ # Patient communication, appointments
|
||||
│ ├── hr/ # Employee management, onboarding
|
||||
│ ├── it/ # Helpdesk, IT service management
|
||||
│ ├── nonprofit/ # Donor management, volunteers
|
||||
│ ├── education/ # Courses, training, LMS
|
||||
│ └── integration/ # External API integrations
|
||||
├── apps/ # HTMX web application templates
|
||||
│ ├── dashboard/ # Analytics and KPI dashboards
|
||||
│ ├── crud/ # Generic CRUD interfaces
|
||||
│ ├── kanban/ # Board-style project management
|
||||
│ ├── admin/ # Admin panel templates
|
||||
│ └── components/ # Reusable UI components
|
||||
├── prompts/ # System prompts and LLM configurations
|
||||
│ ├── assistants/ # Role-specific assistant prompts
|
||||
│ ├── tools/ # Tool description templates
|
||||
│ └── personas/ # Bot personality definitions
|
||||
├── dialogs/ # Reusable BASIC dialog scripts
|
||||
│ ├── auth/ # Authentication flows
|
||||
│ ├── notifications/ # Email, SMS, WhatsApp patterns
|
||||
│ ├── data/ # CRUD operation patterns
|
||||
│ └── integrations/ # API integration patterns
|
||||
└── themes/ # UI themes (.gbtheme)
|
||||
├── default/ # Standard theme
|
||||
├── dark/ # Dark mode theme
|
||||
└── corporate/ # Business-focused themes
|
||||
```
|
||||
| # | Template | Category | Folder | Key Features |
|
||||
|---|----------|----------|--------|--------------|
|
||||
| 1 | Default | Core | `default.gbai` | Minimal starter bot |
|
||||
| 2 | Template | Core | `template.gbai` | Reference implementation |
|
||||
| 3 | AI Search | Search | `ai-search.gbai` | QR codes, document search |
|
||||
| 4 | Announcements | Communications | `announcements.gbai` | Company news, multiple KB |
|
||||
| 5 | Analytics Dashboard | Platform | `analytics-dashboard.gbai` | Metrics, Reports |
|
||||
| 6 | Backup | Platform | `backup.gbai` | Server backup scripts |
|
||||
| 7 | Bank | Finance | `bank.gbai` | Banking services |
|
||||
| 8 | BI | Platform | `bi.gbai` | Dashboards, role separation |
|
||||
| 9 | Bling | Integration | `bling.gbai` | Bling ERP integration |
|
||||
| 10 | Broadcast | Communications | `broadcast.gbai` | Mass messaging |
|
||||
| 11 | Crawler | Search | `crawler.gbai` | Web indexing |
|
||||
| 12 | CRM | Sales | `sales/crm.gbai` | Customer management |
|
||||
| 13 | Attendance CRM | Sales | `sales/attendance-crm.gbai` | Event attendance tracking |
|
||||
| 14 | Marketing | Sales | `sales/marketing.gbai` | Campaign tools |
|
||||
| 15 | Education | Education | `edu.gbai` | Course management |
|
||||
| 16 | ERP | Operations | `erp.gbai` | Process automation |
|
||||
| 17 | HIPAA Medical | Compliance | `compliance/hipaa-medical.gbai` | HIPAA, HITECH |
|
||||
| 18 | Privacy | Compliance | `compliance/privacy.gbai` | LGPD, GDPR, CCPA |
|
||||
| 19 | Law | Legal | `law.gbai` | Document templates |
|
||||
| 20 | LLM Server | AI | `llm-server.gbai` | Model hosting |
|
||||
| 21 | LLM Tools | AI | `llm-tools.gbai` | TOOL-based LLM examples |
|
||||
| 22 | Store | E-commerce | `store.gbai` | Product catalog |
|
||||
| 23 | Talk to Data | Platform | `talk-to-data.gbai` | Natural language SQL |
|
||||
| 24 | WhatsApp | Messaging | `whatsapp.gbai` | WhatsApp Business |
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
## Categories
|
||||
|
||||
### Install a Bot Template
|
||||
### `/sales`
|
||||
Customer relationship and marketing templates.
|
||||
|
||||
```bash
|
||||
# From CLI
|
||||
botserver --install-template crm
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `crm.gbai` | Full CRM system | Leads, Contacts, Accounts, Opportunities |
|
||||
| `marketing.gbai` | Marketing automation | Campaigns, Lead capture, Email sequences |
|
||||
| `attendance-crm.gbai` | Event attendance | Check-ins, Tracking |
|
||||
|
||||
# Or copy manually
|
||||
cp -r bottemplates/bots/sales/crm.gbai /path/to/your/packages/
|
||||
```
|
||||
### `/compliance`
|
||||
Privacy and regulatory compliance templates.
|
||||
|
||||
### Use an App Template
|
||||
| Template | Description | Regulations |
|
||||
|----------|-------------|-------------|
|
||||
| `privacy.gbai` | Data subject rights portal | LGPD, GDPR, CCPA |
|
||||
| `hipaa-medical.gbai` | Healthcare privacy management | HIPAA, HITECH |
|
||||
|
||||
```basic
|
||||
' Generate a CRM app from template
|
||||
CREATE SITE "my-crm", "bottemplates/apps/crud", "Build a customer management system"
|
||||
```
|
||||
### `/platform`
|
||||
Platform administration and analytics templates.
|
||||
|
||||
### Apply a Prompt Template
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `analytics-dashboard.gbai` | Platform analytics bot | Metrics, Reports, AI insights |
|
||||
| `bi.gbai` | Business intelligence | Dashboards, role separation |
|
||||
| `backup.gbai` | Backup automation | Server backup scripts |
|
||||
| `talk-to-data.gbai` | Data queries | Natural language SQL |
|
||||
|
||||
```basic
|
||||
' Load a sales assistant prompt
|
||||
SET SYSTEM PROMPT FROM FILE "bottemplates/prompts/assistants/sales.md"
|
||||
```
|
||||
### `/finance`
|
||||
Financial services templates.
|
||||
|
||||
---
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `bank.gbai` | Banking services | Account management |
|
||||
|
||||
## Bots
|
||||
### `/integration`
|
||||
External API and service integrations.
|
||||
|
||||
Complete `.gbai` packages ready to deploy.
|
||||
| Template | Description | APIs |
|
||||
|----------|-------------|------|
|
||||
| `bling.gbai` | Bling ERP | Brazilian ERP integration |
|
||||
|
||||
### Core
|
||||
### `/productivity`
|
||||
Office and personal productivity templates.
|
||||
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `office.gbai` | Office automation | Document management, Scheduling |
|
||||
| `reminder.gbai` | Reminder system | Scheduled alerts, Follow-ups |
|
||||
|
||||
### `/hr`
|
||||
Human resources templates.
|
||||
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `employees.gbai` | Employee management | Directory, Onboarding |
|
||||
|
||||
### `/it`
|
||||
IT service management templates.
|
||||
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `helpdesk.gbai` | IT helpdesk ticketing | Tickets, Knowledge base |
|
||||
|
||||
### `/healthcare`
|
||||
Healthcare-specific templates.
|
||||
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `patient-comm.gbai` | Patient communication | Appointments, Reminders |
|
||||
|
||||
### `/nonprofit`
|
||||
Nonprofit organization templates.
|
||||
|
||||
| Template | Description | Features |
|
||||
|----------|-------------|----------|
|
||||
| `donor-mgmt.gbai` | Donor management | Donations, Communications |
|
||||
|
||||
### Root Level
|
||||
Core and utility templates.
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `default.gbai` | Minimal starter bot |
|
||||
| `template.gbai` | Reference implementation for creating new bots |
|
||||
|
||||
### Sales & Marketing
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `crm.gbai` | Full CRM with leads, contacts, opportunities |
|
||||
| `marketing.gbai` | Campaign management, email sequences |
|
||||
| `attendance-crm.gbai` | Event attendance tracking |
|
||||
|
||||
### Compliance
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `privacy.gbai` | LGPD, GDPR, CCPA data subject rights |
|
||||
| `hipaa-medical.gbai` | HIPAA/HITECH healthcare compliance |
|
||||
|
||||
### Finance
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `bank.gbai` | Banking services bot |
|
||||
| `invoicing.gbai` | Invoice generation and tracking |
|
||||
|
||||
### Platform
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `analytics-dashboard.gbai` | Platform metrics and reports |
|
||||
| `bi.gbai` | Business intelligence dashboards |
|
||||
| `talk-to-data.gbai` | Natural language SQL queries |
|
||||
| `backup.gbai` | Server backup automation |
|
||||
|
||||
### AI & LLM
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| `llm-server.gbai` | LLM model hosting |
|
||||
| `llm-tools.gbai` | TOOL-based LLM examples |
|
||||
| `default.gbai` | Starter template |
|
||||
| `template.gbai` | Template for creating templates |
|
||||
| `ai-search.gbai` | AI-powered document search |
|
||||
| `announcements.gbai` | Company announcements |
|
||||
| `backup.gbai` | Backup automation |
|
||||
| `broadcast.gbai` | Message broadcasting |
|
||||
| `crawler.gbai` | Web crawling |
|
||||
| `edu.gbai` | Education/training |
|
||||
| `erp.gbai` | ERP integration |
|
||||
| `law.gbai` | Legal document processing |
|
||||
| `llm-server.gbai` | LLM server management |
|
||||
| `llm-tools.gbai` | LLM tool definitions |
|
||||
| `store.gbai` | E-commerce |
|
||||
| `whatsapp.gbai` | WhatsApp-specific features |
|
||||
|
||||
---
|
||||
|
||||
## Apps
|
||||
## Template Structure
|
||||
|
||||
HTMX web application templates for use with `CREATE SITE`.
|
||||
Each `.gbai` template follows this structure:
|
||||
|
||||
### Dashboard Templates
|
||||
|
||||
```basic
|
||||
CREATE SITE "metrics", "bottemplates/apps/dashboard", "
|
||||
Executive dashboard with:
|
||||
- Revenue KPI cards
|
||||
- Monthly trend charts
|
||||
- Top performers table
|
||||
"
|
||||
```
|
||||
|
||||
### CRUD Templates
|
||||
|
||||
```basic
|
||||
CREATE SITE "contacts", "bottemplates/apps/crud", "
|
||||
Contact management with:
|
||||
- Searchable list view
|
||||
- Add/edit forms
|
||||
- Import/export buttons
|
||||
"
|
||||
```
|
||||
|
||||
### Kanban Templates
|
||||
|
||||
```basic
|
||||
CREATE SITE "projects", "bottemplates/apps/kanban", "
|
||||
Project board with:
|
||||
- Drag and drop cards
|
||||
- Status columns
|
||||
- Assignee filtering
|
||||
"
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
Reusable HTML components:
|
||||
|
||||
| Component | File | Description |
|
||||
|-----------|------|-------------|
|
||||
| Data Table | `data-table.html` | Sortable, filterable tables |
|
||||
| Form Modal | `form-modal.html` | Modal dialog with form |
|
||||
| KPI Card | `kpi-card.html` | Metric display card |
|
||||
| Chart | `chart.html` | Chart.js container |
|
||||
| Search | `search-filter.html` | Search with filters |
|
||||
|
||||
---
|
||||
|
||||
## Prompts
|
||||
|
||||
System prompts and LLM configuration templates.
|
||||
|
||||
### Assistant Prompts
|
||||
|
||||
| Prompt | Description |
|
||||
|--------|-------------|
|
||||
| `sales.md` | Sales assistant persona |
|
||||
| `support.md` | Customer support agent |
|
||||
| `analyst.md` | Data analyst assistant |
|
||||
| `developer.md` | Technical assistant |
|
||||
|
||||
### Tool Templates
|
||||
|
||||
Templates for creating LLM-invokable tools:
|
||||
|
||||
```basic
|
||||
' From bottemplates/dialogs/tools/template.bas
|
||||
PARAM input AS STRING DESCRIPTION "Description of input"
|
||||
DESCRIPTION "What this tool does. When to use it."
|
||||
|
||||
' Your tool logic here
|
||||
result = PROCESS input
|
||||
RETURN result
|
||||
template-name.gbai/
|
||||
├── README.md # Template documentation
|
||||
├── template-name.gbdialog/ # BASIC dialog scripts
|
||||
│ ├── start.bas # Entry point
|
||||
│ └── *.bas # Additional dialogs (auto-discovered as TOOLs)
|
||||
├── template-name.gbot/ # Bot configuration
|
||||
│ └── config.csv # Settings
|
||||
├── template-name.gbkb/ # Knowledge base (optional)
|
||||
│ └── docs/ # Documents for RAG
|
||||
├── template-name.gbdrive/ # File storage (optional)
|
||||
└── template-name.gbui/ # Custom UI (optional)
|
||||
└── index.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dialogs
|
||||
## Event-Driven Patterns
|
||||
|
||||
Reusable BASIC script patterns.
|
||||
|
||||
### Authentication
|
||||
Templates should use **ON** triggers instead of polling loops:
|
||||
|
||||
```basic
|
||||
' bottemplates/dialogs/auth/oauth-flow.bas
|
||||
INCLUDE "bottemplates/dialogs/auth/oauth-flow.bas"
|
||||
```
|
||||
|
||||
### Notifications
|
||||
|
||||
```basic
|
||||
' Send multi-channel notification
|
||||
INCLUDE "bottemplates/dialogs/notifications/multi-channel.bas"
|
||||
NOTIFY user, "Your order shipped!", channels: ["email", "whatsapp"]
|
||||
```
|
||||
|
||||
### Data Operations
|
||||
|
||||
```basic
|
||||
' CRUD with validation
|
||||
INCLUDE "bottemplates/dialogs/data/validated-crud.bas"
|
||||
CREATE_VALIDATED "contacts", contact_data, validation_rules
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Themes
|
||||
|
||||
UI customization packages.
|
||||
|
||||
### Using a Theme
|
||||
|
||||
```
|
||||
my-bot.gbai/
|
||||
└── my-bot.gbtheme/ # Copy theme files here
|
||||
├── colors.css
|
||||
├── fonts.css
|
||||
└── components.css
|
||||
```
|
||||
|
||||
### Available Themes
|
||||
|
||||
| Theme | Description |
|
||||
|-------|-------------|
|
||||
| `default` | Clean, modern appearance |
|
||||
| `dark` | Dark mode with accent colors |
|
||||
| `corporate` | Professional business styling |
|
||||
| `minimal` | Stripped-down, fast-loading |
|
||||
|
||||
---
|
||||
|
||||
## Bot Package Structure
|
||||
|
||||
Every `.gbai` bot follows this structure:
|
||||
|
||||
```
|
||||
my-bot.gbai/
|
||||
├── README.md # Documentation
|
||||
├── my-bot.gbdialog/ # BASIC scripts
|
||||
│ ├── start.bas # Entry point
|
||||
│ └── tools/ # LLM-invokable tools
|
||||
├── my-bot.gbot/ # Configuration
|
||||
│ └── config.csv # Bot settings
|
||||
├── my-bot.gbkb/ # Knowledge base (optional)
|
||||
│ └── docs/ # Documents for RAG
|
||||
├── my-bot.gbdrive/ # File storage (optional)
|
||||
└── my-bot.gbtheme/ # UI customization (optional)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Event-Driven Design
|
||||
|
||||
Use `ON` triggers instead of polling:
|
||||
|
||||
```basic
|
||||
' ✅ Good - Event-driven
|
||||
ON INSERT OF "leads"
|
||||
lead = GET LAST "leads"
|
||||
NOTIFY sales_team, "New lead: " + lead.name
|
||||
END ON
|
||||
|
||||
' ❌ Bad - Polling loop
|
||||
' ❌ OLD WAY - Polling (avoid)
|
||||
mainLoop:
|
||||
leads = FIND "leads", "new = true"
|
||||
leads = FIND "leads", "processed = false"
|
||||
WAIT 5
|
||||
GOTO mainLoop
|
||||
|
||||
' ✅ NEW WAY - Event-Driven
|
||||
ON INSERT OF "leads"
|
||||
lead = GET LAST "leads"
|
||||
score = SCORE LEAD lead
|
||||
TALK TO "whatsapp:" + sales_phone, "New lead: " + lead.name
|
||||
END ON
|
||||
```
|
||||
|
||||
### LLM-Invokable Tools
|
||||
---
|
||||
|
||||
Add `PARAM` and `DESCRIPTION` to make scripts callable by LLM:
|
||||
## TOOL-Based LLM Integration
|
||||
|
||||
Every `.bas` file with `PARAM` and `DESCRIPTION` becomes an LLM-invokable tool:
|
||||
|
||||
```basic
|
||||
' score-lead.bas
|
||||
PARAM email AS STRING LIKE "john@example.com" DESCRIPTION "Lead email"
|
||||
PARAM company AS STRING DESCRIPTION "Company name"
|
||||
PARAM email AS STRING LIKE "john@company.com" DESCRIPTION "Lead email"
|
||||
PARAM name AS STRING LIKE "John Smith" DESCRIPTION "Lead name"
|
||||
|
||||
DESCRIPTION "Score and qualify a sales lead"
|
||||
DESCRIPTION "Score a new lead. Use when user mentions a prospect."
|
||||
|
||||
score = AI SCORE LEAD email, company
|
||||
RETURN score
|
||||
```
|
||||
lead = NEW OBJECT
|
||||
lead.email = email
|
||||
lead.name = name
|
||||
|
||||
### HTMX Patterns
|
||||
score = AI SCORE LEAD lead
|
||||
|
||||
Generated apps should use HTMX for server communication:
|
||||
IF score.status = "hot" THEN
|
||||
TALK TO "whatsapp:+5511999887766", "🔥 Hot lead: " + name
|
||||
END IF
|
||||
|
||||
```html
|
||||
<div hx-get="/api/data/leads"
|
||||
hx-trigger="load, every 30s"
|
||||
hx-swap="innerHTML">
|
||||
Loading...
|
||||
</div>
|
||||
TALK "Lead scored: " + score.score + "/100"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
## Installation
|
||||
|
||||
1. Fork this repository
|
||||
2. Create your template following the structure
|
||||
3. Test thoroughly
|
||||
4. Add documentation
|
||||
5. Submit a pull request
|
||||
### From Console
|
||||
|
||||
### Template Checklist
|
||||
```bash
|
||||
botserver --install-template crm
|
||||
```
|
||||
|
||||
- [ ] Follows naming conventions
|
||||
- [ ] Includes README.md
|
||||
- [ ] Uses event-driven patterns
|
||||
- [ ] Has proper error handling
|
||||
- [ ] Documented configuration options
|
||||
- [ ] Example usage provided
|
||||
### From BASIC
|
||||
|
||||
```basic
|
||||
INSTALL TEMPLATE "crm"
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
Copy the template folder to your bot's packages directory:
|
||||
|
||||
```bash
|
||||
cp -r templates/sales/crm.gbai /path/to/your/bot/packages/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom Templates
|
||||
|
||||
1. Copy `template.gbai` as a starting point
|
||||
2. Rename the folder to `your-template.gbai`
|
||||
3. Update internal folder names to match
|
||||
4. Edit `config.csv` with your bot settings
|
||||
5. Create dialog scripts in the `.gbdialog` folder
|
||||
6. Use **ON** triggers instead of polling loops
|
||||
7. Add `PARAM` and `DESCRIPTION` to make scripts LLM-invokable
|
||||
8. Add documentation in `README.md`
|
||||
|
||||
### Template Best Practices
|
||||
|
||||
- Use `ON` for event-driven automation
|
||||
- Use `TALK TO` for multi-channel notifications
|
||||
- Use `LLM` for intelligent decision-making
|
||||
- Use `SCORE LEAD` / `AI SCORE LEAD` for lead qualification
|
||||
- Create `.bas` files with `DESCRIPTION` for LLM tool discovery
|
||||
- Log activities for audit trails
|
||||
- Include error handling
|
||||
- Document all configuration options
|
||||
|
||||
---
|
||||
|
||||
## Contributing Templates
|
||||
|
||||
1. Create your template following the structure above
|
||||
2. Test thoroughly with different inputs
|
||||
3. Document all features and configuration
|
||||
4. Submit a pull request with:
|
||||
- Template files
|
||||
- Updated category README
|
||||
- Entry in this document
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - Part of General Bots Open Source Platform
|
||||
All templates are licensed under AGPL-3.0 as part of General Bots.
|
||||
|
||||
---
|
||||
|
||||
**Pragmatismo** - [pragmatismo.com.br](https://pragmatismo.com.br)
|
||||
**Pragmatismo** - General Bots Open Source Platform
|
||||
13
ai-search.gbai/ai-search.gbdialog/qr.bas
Normal file
13
ai-search.gbai/ai-search.gbdialog/qr.bas
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
PARAM doc AS QRCODE LIKE "photo of QR code" DESCRIPTION "QR Code image to scan and load document"
|
||||
|
||||
DESCRIPTION "Scan a QR Code to load and query a document"
|
||||
|
||||
text = GET doc
|
||||
|
||||
IF text THEN
|
||||
SET CONTEXT "Based on this document, answer the person's questions:\n\n" + text
|
||||
TALK "Document ${doc} loaded. You can ask me anything about it."
|
||||
SEND FILE doc
|
||||
ELSE
|
||||
TALK "Document not found, please try again."
|
||||
END IF
|
||||
27
ai-search.gbai/ai-search.gbdialog/start.bas
Normal file
27
ai-search.gbai/ai-search.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
ADD TOOL "qr"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
ADD SUGGESTION "scan" AS "Scan a QR Code"
|
||||
ADD SUGGESTION "find" AS "Find a procedure"
|
||||
ADD SUGGESTION "help" AS "How to search documents"
|
||||
|
||||
BEGIN TALK
|
||||
General Bots AI Search
|
||||
|
||||
Comprehensive Document Search with AI summaries and EDM integration.
|
||||
|
||||
**Options:**
|
||||
• Scan a QR Code - Send a photo to scan
|
||||
• Find a Procedure - Ask about any process
|
||||
|
||||
**Examples:**
|
||||
- How to send a fax?
|
||||
- How to clean the machine?
|
||||
- How to find a contact?
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a document search assistant. Help users find procedures and information from documents.
|
||||
When users want to scan QR codes, use the qr tool.
|
||||
Provide clear, concise answers based on document content.
|
||||
END SYSTEM PROMPT
|
||||
BIN
ai-search.gbai/ai-search.gbdrive/manuals/42LB5800.pdf
Normal file
BIN
ai-search.gbai/ai-search.gbdrive/manuals/42LB5800.pdf
Normal file
Binary file not shown.
1
ai-search.gbai/ai-search.gbkb/docs/README.md
Normal file
1
ai-search.gbai/ai-search.gbkb/docs/README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
O céu é azul-lilás e pronto.
|
||||
BIN
ai-search.gbai/ai-search.gbkb/docs/default.pdf
Normal file
BIN
ai-search.gbai/ai-search.gbkb/docs/default.pdf
Normal file
Binary file not shown.
5
ai-search.gbai/ai-search.gbot/config.csv
Normal file
5
ai-search.gbai/ai-search.gbot/config.csv
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
name,value
|
||||
theme-color1, #0d2111
|
||||
theme-color2, #fff111
|
||||
theme-logo, https://pragmatismo.com.br/icons/general-bots.svg
|
||||
theme-title, AI Search General Bots
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
REM Analytics Dashboard Start Dialog
|
||||
REM Displays pre-computed statistics from update-stats.bas
|
||||
REM No heavy computation at conversation start
|
||||
|
||||
DESCRIPTION "View knowledge base analytics and statistics"
|
||||
|
||||
REM Load pre-computed values from BOT MEMORY
|
||||
totalDocs = GET BOT MEMORY("analytics_total_docs")
|
||||
totalVectors = GET BOT MEMORY("analytics_total_vectors")
|
||||
storageMB = GET BOT MEMORY("analytics_storage_mb")
|
||||
collections = GET BOT MEMORY("analytics_collections")
|
||||
docsWeek = GET BOT MEMORY("analytics_docs_week")
|
||||
docsMonth = GET BOT MEMORY("analytics_docs_month")
|
||||
growthRate = GET BOT MEMORY("analytics_growth_rate")
|
||||
healthPercent = GET BOT MEMORY("analytics_health_percent")
|
||||
lastUpdate = GET BOT MEMORY("analytics_last_update")
|
||||
summary = GET BOT MEMORY("analytics_summary")
|
||||
|
||||
REM Set contexts for different report types
|
||||
SET CONTEXT "overview" AS "Total documents: " + totalDocs + ", Storage: " + storageMB + " MB, Collections: " + collections
|
||||
SET CONTEXT "activity" AS "Documents added this week: " + docsWeek + ", This month: " + docsMonth + ", Growth rate: " + growthRate + "%"
|
||||
SET CONTEXT "health" AS "System health: " + healthPercent + "%, Last updated: " + lastUpdate
|
||||
|
||||
REM Clear and set up suggestions
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "overview" AS "Show overview"
|
||||
ADD SUGGESTION "overview" AS "Storage usage"
|
||||
ADD SUGGESTION "activity" AS "Recent activity"
|
||||
ADD SUGGESTION "activity" AS "Growth trends"
|
||||
ADD SUGGESTION "health" AS "System health"
|
||||
ADD SUGGESTION "health" AS "Collection status"
|
||||
|
||||
REM Add tools for detailed reports
|
||||
ADD TOOL "detailed-report"
|
||||
ADD TOOL "export-stats"
|
||||
|
||||
REM Welcome message with pre-computed summary
|
||||
IF summary <> "" THEN
|
||||
TALK summary
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
TALK "📊 **Analytics Dashboard**"
|
||||
TALK ""
|
||||
|
||||
IF totalDocs <> "" THEN
|
||||
TALK "**Knowledge Base Overview**"
|
||||
TALK "• Documents: " + FORMAT(totalDocs, "#,##0")
|
||||
TALK "• Vectors: " + FORMAT(totalVectors, "#,##0")
|
||||
TALK "• Storage: " + FORMAT(storageMB, "#,##0.00") + " MB"
|
||||
TALK "• Collections: " + collections
|
||||
TALK ""
|
||||
|
||||
TALK "**Recent Activity**"
|
||||
TALK "• This week: +" + FORMAT(docsWeek, "#,##0") + " documents"
|
||||
TALK "• This month: +" + FORMAT(docsMonth, "#,##0") + " documents"
|
||||
|
||||
IF growthRate <> "" THEN
|
||||
IF growthRate > 0 THEN
|
||||
TALK "• Trend: 📈 +" + FORMAT(growthRate, "#,##0.0") + "% vs average"
|
||||
ELSE
|
||||
TALK "• Trend: 📉 " + FORMAT(growthRate, "#,##0.0") + "% vs average"
|
||||
END IF
|
||||
END IF
|
||||
TALK ""
|
||||
|
||||
IF healthPercent <> "" THEN
|
||||
IF healthPercent = 100 THEN
|
||||
TALK "✅ All systems healthy"
|
||||
ELSE
|
||||
TALK "⚠️ System health: " + FORMAT(healthPercent, "#,##0") + "%"
|
||||
END IF
|
||||
END IF
|
||||
ELSE
|
||||
TALK "Statistics are being computed. Please check back in a few minutes."
|
||||
TALK "Run the update-stats schedule to refresh data."
|
||||
END IF
|
||||
|
||||
TALK ""
|
||||
TALK "Ask me about any metric or select a topic above."
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
REM Analytics Statistics Update
|
||||
REM Runs hourly to pre-compute dashboard statistics
|
||||
REM Similar pattern to announcements/update-summary.bas
|
||||
|
||||
SET SCHEDULE "0 * * * *"
|
||||
|
||||
REM Fetch KB statistics
|
||||
stats = KB STATISTICS
|
||||
statsObj = JSON PARSE stats
|
||||
|
||||
REM Store document counts
|
||||
SET BOT MEMORY "analytics_total_docs", statsObj.total_documents
|
||||
SET BOT MEMORY "analytics_total_vectors", statsObj.total_vectors
|
||||
SET BOT MEMORY "analytics_storage_mb", statsObj.total_disk_size_mb
|
||||
SET BOT MEMORY "analytics_collections", statsObj.total_collections
|
||||
|
||||
REM Store activity metrics
|
||||
SET BOT MEMORY "analytics_docs_week", statsObj.documents_added_last_week
|
||||
SET BOT MEMORY "analytics_docs_month", statsObj.documents_added_last_month
|
||||
|
||||
REM Calculate growth rate
|
||||
IF statsObj.documents_added_last_month > 0 THEN
|
||||
weeklyAvg = statsObj.documents_added_last_month / 4
|
||||
IF weeklyAvg > 0 THEN
|
||||
growthRate = ((statsObj.documents_added_last_week - weeklyAvg) / weeklyAvg) * 100
|
||||
SET BOT MEMORY "analytics_growth_rate", growthRate
|
||||
END IF
|
||||
END IF
|
||||
|
||||
REM Check collection health
|
||||
healthyCount = 0
|
||||
totalCount = 0
|
||||
FOR EACH coll IN statsObj.collections
|
||||
totalCount = totalCount + 1
|
||||
IF coll.status = "green" THEN
|
||||
healthyCount = healthyCount + 1
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
IF totalCount > 0 THEN
|
||||
healthPercent = (healthyCount / totalCount) * 100
|
||||
SET BOT MEMORY "analytics_health_percent", healthPercent
|
||||
END IF
|
||||
|
||||
REM Store last update timestamp
|
||||
SET BOT MEMORY "analytics_last_update", NOW()
|
||||
|
||||
REM Generate summary for quick display
|
||||
summary = "📊 " + FORMAT(statsObj.total_documents, "#,##0") + " docs"
|
||||
summary = summary + " | " + FORMAT(statsObj.total_disk_size_mb, "#,##0.0") + " MB"
|
||||
summary = summary + " | +" + FORMAT(statsObj.documents_added_last_week, "#,##0") + " this week"
|
||||
SET BOT MEMORY "analytics_summary", summary
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
# Analytics Dashboard Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Analytics Dashboard provides real-time insights into your knowledge base performance, document statistics, and system health metrics.
|
||||
|
||||
## Key Metrics
|
||||
|
||||
### Document Statistics
|
||||
- **Total Documents**: Number of documents indexed in your knowledge base
|
||||
- **Total Vectors**: Number of vector embeddings created for semantic search
|
||||
- **Storage Used**: Total storage consumption in megabytes
|
||||
- **Collections**: Number of document collections/categories
|
||||
|
||||
### Activity Metrics
|
||||
- **Documents This Week**: New documents added in the current week
|
||||
- **Documents This Month**: New documents added in the current month
|
||||
- **Growth Rate**: Percentage change compared to historical average
|
||||
|
||||
### System Health
|
||||
- **Health Percentage**: Overall system status (100% = all systems operational)
|
||||
- **Last Update**: Timestamp of the most recent statistics refresh
|
||||
|
||||
## How to Use
|
||||
|
||||
### Viewing Overview
|
||||
Ask for an overview to see all key metrics at a glance:
|
||||
- "Show overview"
|
||||
- "What's the storage usage?"
|
||||
- "How many documents do we have?"
|
||||
|
||||
### Checking Activity
|
||||
Monitor recent changes and growth trends:
|
||||
- "Show recent activity"
|
||||
- "What's the growth trend?"
|
||||
- "How many documents were added this week?"
|
||||
|
||||
### System Health
|
||||
Check if all systems are running properly:
|
||||
- "Show system health"
|
||||
- "What's the collection status?"
|
||||
- "Is everything working?"
|
||||
|
||||
## Understanding the Data
|
||||
|
||||
### Growth Rate Interpretation
|
||||
- **Positive rate (📈)**: Knowledge base is growing faster than average
|
||||
- **Negative rate (📉)**: Growth has slowed compared to average
|
||||
- **Zero rate**: Stable growth matching historical patterns
|
||||
|
||||
### Health Status
|
||||
- **100%**: All systems operational
|
||||
- **Below 100%**: Some components may need attention
|
||||
- **Check specific warnings** for details on affected systems
|
||||
|
||||
## Scheduled Updates
|
||||
|
||||
Statistics are automatically refreshed by the `update-stats` scheduled job. By default, this runs:
|
||||
- Every hour for activity metrics
|
||||
- Daily for comprehensive statistics
|
||||
- On-demand when requested
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: Why do I see "Statistics are being computed"?**
|
||||
A: The analytics system is initializing or refreshing data. Wait a few minutes and try again.
|
||||
|
||||
**Q: How accurate are the metrics?**
|
||||
A: Metrics are updated regularly and reflect the state at the last refresh time shown in "Last Update".
|
||||
|
||||
**Q: Can I export analytics data?**
|
||||
A: Yes, use the "Export Stats" tool to download metrics in various formats.
|
||||
|
||||
**Q: What affects system health?**
|
||||
A: Health reflects database connectivity, vector store status, storage availability, and background job status.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Monitoring**: Check the dashboard weekly to track growth trends
|
||||
2. **Storage Planning**: Monitor storage usage to plan capacity needs
|
||||
3. **Activity Tracking**: Use activity metrics to measure content team productivity
|
||||
4. **Health Alerts**: Address health issues promptly when below 100%
|
||||
7
announcements.gbai/annoucements.gbot/config.csv
Normal file
7
announcements.gbai/annoucements.gbot/config.csv
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
name,value
|
||||
episodic-memory-history,2
|
||||
episodic-memory-threshold,4
|
||||
theme-color1,#0d2b55
|
||||
theme-color2,#fff9c2
|
||||
theme-logo,https://pragmatismo.com.br/icons/general-bots.svg
|
||||
theme-title,Announcements General Bots
|
||||
|
0
announcements.gbai/announcements.gbdialog/auth.bas
Normal file
0
announcements.gbai/announcements.gbdialog/auth.bas
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
PARAM subject AS STRING LIKE "circular" DESCRIPTION "Subject to switch conversation to: circular, comunicado, or geral"
|
||||
|
||||
DESCRIPTION "Switch conversation subject when user wants to change topic"
|
||||
|
||||
kbname = LLM "Return single word: circular, comunicado or geral based on: " + subject
|
||||
ADD_KB kbname
|
||||
|
||||
TALK "Subject changed to " + subject
|
||||
21
announcements.gbai/announcements.gbdialog/start.bas
Normal file
21
announcements.gbai/announcements.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
resume1 = GET BOT MEMORY("resume")
|
||||
resume2 = GET BOT MEMORY("auxiliom")
|
||||
resume3 = GET BOT MEMORY("toolbix")
|
||||
|
||||
SET CONTEXT "general" AS resume1
|
||||
SET CONTEXT "auxiliom" AS resume2
|
||||
SET CONTEXT "toolbix" AS resume3
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "general" AS "Weekly announcements"
|
||||
ADD SUGGESTION "general" AS "Latest circulars"
|
||||
ADD SUGGESTION "auxiliom" AS "What is Auxiliom?"
|
||||
ADD SUGGESTION "auxiliom" AS "Auxiliom services"
|
||||
ADD SUGGESTION "toolbix" AS "Toolbix features"
|
||||
ADD SUGGESTION "toolbix" AS "Toolbix for business"
|
||||
|
||||
ADD TOOL "change-subject"
|
||||
|
||||
TALK resume1
|
||||
TALK "Ask me about any announcement or circular."
|
||||
11
announcements.gbai/announcements.gbdialog/update-summary.bas
Normal file
11
announcements.gbai/announcements.gbdialog/update-summary.bas
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
SET SCHEDULE "59 * * * *"
|
||||
|
||||
text = GET "announcements.gbkb/news/news.pdf"
|
||||
resume = LLM "In a few words, resume this: " + text
|
||||
SET BOT MEMORY "resume", resume
|
||||
|
||||
text1 = GET "announcements.gbkb/auxiliom/auxiliom.pdf"
|
||||
SET BOT MEMORY "auxiliom", text1
|
||||
|
||||
text2 = GET "announcements.gbkb/toolbix/toolbix.pdf"
|
||||
SET BOT MEMORY "toolbix", text2
|
||||
BIN
announcements.gbai/announcements.gbkb/auxiliom/auxiliom.pdf
Normal file
BIN
announcements.gbai/announcements.gbkb/auxiliom/auxiliom.pdf
Normal file
Binary file not shown.
BIN
announcements.gbai/announcements.gbkb/news/news.pdf
Normal file
BIN
announcements.gbai/announcements.gbkb/news/news.pdf
Normal file
Binary file not shown.
BIN
announcements.gbai/announcements.gbkb/toolbix/toolbix.pdf
Normal file
BIN
announcements.gbai/announcements.gbkb/toolbix/toolbix.pdf
Normal file
Binary file not shown.
730
auth/login.html
Normal file
730
auth/login.html
Normal file
|
|
@ -0,0 +1,730 @@
|
|||
<!doctype html>
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Login - BotServer</title>
|
||||
<script src="/static/js/vendor/htmx.min.js"></script>
|
||||
<script src="/static/js/vendor/htmx-ws.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #3b82f6;
|
||||
--primary-hover: #2563eb;
|
||||
--secondary: #64748b;
|
||||
--background: #ffffff;
|
||||
--surface: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--error: #ef4444;
|
||||
--success: #10b981;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--background: #0f172a;
|
||||
--surface: #1e293b;
|
||||
--text: #f1f5f9;
|
||||
--text-secondary: #94a3b8;
|
||||
--border: #334155;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
Oxygen, Ubuntu, sans-serif;
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: var(--surface);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
input[type="email"],
|
||||
input[type="password"],
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-right: 0.5rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.divider::before,
|
||||
.divider::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.divider span {
|
||||
padding: 0 1rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.oauth-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-oauth {
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-oauth:hover {
|
||||
background: var(--surface);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-oauth.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-oauth svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Provider-specific colors on hover */
|
||||
.btn-oauth-google:hover {
|
||||
border-color: #ea4335;
|
||||
color: #ea4335;
|
||||
}
|
||||
|
||||
.btn-oauth-discord:hover {
|
||||
border-color: #5865f2;
|
||||
color: #5865f2;
|
||||
}
|
||||
|
||||
.btn-oauth-reddit:hover {
|
||||
border-color: #ff4500;
|
||||
color: #ff4500;
|
||||
}
|
||||
|
||||
.btn-oauth-twitter:hover {
|
||||
border-color: #1da1f2;
|
||||
color: #1da1f2;
|
||||
}
|
||||
|
||||
.btn-oauth-microsoft:hover {
|
||||
border-color: #00a4ef;
|
||||
color: #00a4ef;
|
||||
}
|
||||
|
||||
.btn-oauth-facebook:hover {
|
||||
border-color: #1877f2;
|
||||
color: #1877f2;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: rgb(239 68 68 / 0.1);
|
||||
color: var(--error);
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.875rem;
|
||||
border: 1px solid rgb(239 68 68 / 0.2);
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: rgb(16 185 129 / 0.1);
|
||||
color: var(--success);
|
||||
padding: 0.75rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.875rem;
|
||||
border: 1px solid rgb(16 185 129 / 0.2);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: none;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-top-color: white;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.6s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.htmx-request .loading-spinner {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.htmx-request .btn-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.25rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.dev-mode-banner {
|
||||
background: rgb(251 146 60 / 0.1);
|
||||
color: rgb(234 88 12);
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
font-size: 0.875rem;
|
||||
border-bottom: 1px solid rgb(251 146 60 / 0.2);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.oauth-section {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.oauth-loading {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.no-oauth {
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Theme Toggle -->
|
||||
<button
|
||||
class="theme-toggle"
|
||||
onclick="toggleTheme()"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<span id="theme-icon">🌙</span>
|
||||
</button>
|
||||
|
||||
<!-- Dev Mode Banner (shown when Zitadel is not available) -->
|
||||
<div id="dev-mode-banner" class="dev-mode-banner" style="display: none">
|
||||
⚠️ Development Mode: Use any email with password "password"
|
||||
</div>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-card">
|
||||
<!-- Logo -->
|
||||
<div class="logo">
|
||||
<div class="logo-icon">🤖</div>
|
||||
<div class="logo-text">BotServer</div>
|
||||
</div>
|
||||
|
||||
<h1>Sign in to your account</h1>
|
||||
|
||||
<!-- Error Message -->
|
||||
{% if error_message %}
|
||||
<div class="error-message">{{ error_message }}</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Success Message Target -->
|
||||
<div id="message-container"></div>
|
||||
|
||||
<!-- Login Form -->
|
||||
<form
|
||||
id="login-form"
|
||||
hx-post="/auth/login"
|
||||
hx-target="#message-container"
|
||||
hx-indicator=".loading-spinner"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label for="email">Email address</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
required
|
||||
autocomplete="email"
|
||||
placeholder="user@example.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="remember_me"
|
||||
name="remember_me"
|
||||
value="true"
|
||||
/>
|
||||
<label for="remember_me" class="checkbox-label"
|
||||
>Remember me</label
|
||||
>
|
||||
</div>
|
||||
|
||||
{% if redirect_url %}
|
||||
<input
|
||||
type="hidden"
|
||||
name="redirect"
|
||||
value="{{ redirect_url }}"
|
||||
/>
|
||||
{% endif %}
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="btn-text">Sign in</span>
|
||||
<span class="loading-spinner"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- OAuth Options -->
|
||||
<div class="divider">
|
||||
<span>or continue with</span>
|
||||
</div>
|
||||
|
||||
<div class="oauth-section" id="oauth-section">
|
||||
<div class="oauth-loading" id="oauth-loading">
|
||||
Loading login options...
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="oauth-grid"
|
||||
id="oauth-buttons"
|
||||
style="display: none"
|
||||
>
|
||||
<!-- Google -->
|
||||
<a
|
||||
href="/auth/oauth/google{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-google hidden"
|
||||
id="btn-google"
|
||||
title="Sign in with Google"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
fill="#4285F4"
|
||||
/>
|
||||
<path
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
fill="#34A853"
|
||||
/>
|
||||
<path
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
fill="#FBBC05"
|
||||
/>
|
||||
<path
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
fill="#EA4335"
|
||||
/>
|
||||
</svg>
|
||||
Google
|
||||
</a>
|
||||
|
||||
<!-- Microsoft -->
|
||||
<a
|
||||
href="/auth/oauth/microsoft{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-microsoft hidden"
|
||||
id="btn-microsoft"
|
||||
title="Sign in with Microsoft"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M11.4 24H0V12.6h11.4V24z"
|
||||
fill="#00A4EF"
|
||||
/>
|
||||
<path
|
||||
d="M24 24H12.6V12.6H24V24z"
|
||||
fill="#FFB900"
|
||||
/>
|
||||
<path
|
||||
d="M11.4 11.4H0V0h11.4v11.4z"
|
||||
fill="#F25022"
|
||||
/>
|
||||
<path
|
||||
d="M24 11.4H12.6V0H24v11.4z"
|
||||
fill="#7FBA00"
|
||||
/>
|
||||
</svg>
|
||||
Microsoft
|
||||
</a>
|
||||
|
||||
<!-- Discord -->
|
||||
<a
|
||||
href="/auth/oauth/discord{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-discord hidden"
|
||||
id="btn-discord"
|
||||
title="Sign in with Discord"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"
|
||||
fill="#5865F2"
|
||||
/>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
|
||||
<!-- Facebook -->
|
||||
<a
|
||||
href="/auth/oauth/facebook{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-facebook hidden"
|
||||
id="btn-facebook"
|
||||
title="Sign in with Facebook"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"
|
||||
fill="#1877F2"
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
|
||||
<!-- Twitter/X -->
|
||||
<a
|
||||
href="/auth/oauth/twitter{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-twitter hidden"
|
||||
id="btn-twitter"
|
||||
title="Sign in with Twitter/X"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
|
||||
/>
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
|
||||
<!-- Reddit -->
|
||||
<a
|
||||
href="/auth/oauth/reddit{% if redirect_url %}?redirect={{ redirect_url }}{% endif %}"
|
||||
class="btn btn-oauth btn-oauth-reddit hidden"
|
||||
id="btn-reddit"
|
||||
title="Sign in with Reddit"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"
|
||||
fill="#FF4500"
|
||||
/>
|
||||
</svg>
|
||||
Reddit
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="no-oauth" id="no-oauth" style="display: none">
|
||||
No social login providers configured
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer Links -->
|
||||
<div class="footer-links">
|
||||
<a href="/auth/forgot-password" class="link"
|
||||
>Forgot password?</a
|
||||
>
|
||||
<span> · </span>
|
||||
<a href="/auth/register" class="link">Create account</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Theme management
|
||||
function initTheme() {
|
||||
const savedTheme = localStorage.getItem("theme") || "light";
|
||||
document.documentElement.setAttribute("data-theme", savedTheme);
|
||||
updateThemeIcon(savedTheme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const currentTheme =
|
||||
document.documentElement.getAttribute("data-theme");
|
||||
const newTheme = currentTheme === "light" ? "dark" : "light";
|
||||
document.documentElement.setAttribute("data-theme", newTheme);
|
||||
localStorage.setItem("theme", newTheme);
|
||||
updateThemeIcon(newTheme);
|
||||
}
|
||||
|
||||
function updateThemeIcon(theme) {
|
||||
const icon = document.getElementById("theme-icon");
|
||||
icon.textContent = theme === "light" ? "🌙" : "☀️";
|
||||
}
|
||||
|
||||
// Check if in development mode
|
||||
async function checkDevMode() {
|
||||
try {
|
||||
const response = await fetch("/api/auth/mode");
|
||||
const data = await response.json();
|
||||
if (data.mode === "development") {
|
||||
document.getElementById(
|
||||
"dev-mode-banner",
|
||||
).style.display = "block";
|
||||
document.body.style.paddingTop = "2.5rem";
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore - assume production mode
|
||||
}
|
||||
}
|
||||
|
||||
// Load enabled OAuth providers
|
||||
async function loadOAuthProviders() {
|
||||
const loadingEl = document.getElementById("oauth-loading");
|
||||
const buttonsEl = document.getElementById("oauth-buttons");
|
||||
const noOAuthEl = document.getElementById("no-oauth");
|
||||
|
||||
try {
|
||||
const response = await fetch("/auth/oauth/providers");
|
||||
const data = await response.json();
|
||||
|
||||
loadingEl.style.display = "none";
|
||||
|
||||
if (data.providers && data.providers.length > 0) {
|
||||
buttonsEl.style.display = "grid";
|
||||
|
||||
// Show buttons for enabled providers
|
||||
data.providers.forEach((provider) => {
|
||||
const btn = document.getElementById(
|
||||
"btn-" + provider.id,
|
||||
);
|
||||
if (btn) {
|
||||
btn.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// Check if any buttons are visible
|
||||
const visibleButtons =
|
||||
buttonsEl.querySelectorAll("a:not(.hidden)");
|
||||
if (visibleButtons.length === 0) {
|
||||
buttonsEl.style.display = "none";
|
||||
noOAuthEl.style.display = "block";
|
||||
}
|
||||
} else {
|
||||
noOAuthEl.style.display = "block";
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load OAuth providers:", err);
|
||||
loadingEl.style.display = "none";
|
||||
// Show all buttons as fallback (they'll redirect to error page if not configured)
|
||||
buttonsEl.style.display = "grid";
|
||||
buttonsEl.querySelectorAll("a").forEach((btn) => {
|
||||
btn.classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
initTheme();
|
||||
checkDevMode();
|
||||
loadOAuthProviders();
|
||||
|
||||
// Handle form validation
|
||||
const form = document.getElementById("login-form");
|
||||
form.addEventListener("submit", (e) => {
|
||||
const email = document.getElementById("email").value;
|
||||
const password = document.getElementById("password").value;
|
||||
|
||||
if (!email || !password) {
|
||||
e.preventDefault();
|
||||
showError("Please fill in all fields");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Show error message
|
||||
function showError(message) {
|
||||
const container = document.getElementById("message-container");
|
||||
container.innerHTML = `<div class="error-message">${message}</div>`;
|
||||
}
|
||||
|
||||
// Handle HTMX events
|
||||
document.body.addEventListener("htmx:afterRequest", (event) => {
|
||||
if (event.detail.xhr.status === 200) {
|
||||
// Check if we got a redirect header
|
||||
const redirect =
|
||||
event.detail.xhr.getResponseHeader("HX-Redirect");
|
||||
if (redirect) {
|
||||
window.location.href = redirect;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener("htmx:responseError", (event) => {
|
||||
showError(
|
||||
"Authentication failed. Please check your credentials.",
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
42
backup.gbai/backup.gbdialog/backup-to-server.bas
Normal file
42
backup.gbai/backup.gbdialog/backup-to-server.bas
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
PARAM folder AS STRING LIKE "default.gbdrive" DESCRIPTION "Folder to backup files from" OPTIONAL
|
||||
PARAM days_old AS INTEGER LIKE 3 DESCRIPTION "Archive files older than this many days" OPTIONAL
|
||||
|
||||
DESCRIPTION "Backup and archive expired files to server storage"
|
||||
|
||||
IF NOT folder THEN
|
||||
folder = "default.gbdrive"
|
||||
END IF
|
||||
|
||||
IF NOT days_old THEN
|
||||
days_old = 3
|
||||
END IF
|
||||
|
||||
list = DIR folder
|
||||
archived = 0
|
||||
|
||||
FOR EACH item IN list
|
||||
oldDays = DATEDIFF today, item.modified, "day"
|
||||
|
||||
IF oldDays > days_old THEN
|
||||
blob = UPLOAD item
|
||||
|
||||
WITH logEntry
|
||||
action = "archived"
|
||||
date = today
|
||||
time = now
|
||||
path = item.path
|
||||
name = item.name
|
||||
size = item.size
|
||||
modified = item.modified
|
||||
md5 = blob.md5
|
||||
END WITH
|
||||
|
||||
SAVE "log.xlsx", logEntry
|
||||
DELETE item
|
||||
archived = archived + 1
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
TALK "Backup complete. " + archived + " files archived."
|
||||
|
||||
RETURN archived
|
||||
34
backup.gbai/backup.gbdialog/start.bas
Normal file
34
backup.gbai/backup.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
ADD TOOL "backup-to-server"
|
||||
ADD TOOL "restore-file"
|
||||
ADD TOOL "list-archived"
|
||||
ADD TOOL "cleanup-old"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "backup" AS "Run backup now"
|
||||
ADD SUGGESTION "list" AS "View archived files"
|
||||
ADD SUGGESTION "restore" AS "Restore a file"
|
||||
ADD SUGGESTION "status" AS "Backup status"
|
||||
|
||||
SET CONTEXT "backup" AS "You are a backup management assistant. Help users archive files to server storage, restore archived files, and manage backup schedules."
|
||||
|
||||
BEGIN TALK
|
||||
**Backup Manager**
|
||||
|
||||
I can help you with:
|
||||
• Archive files to server storage
|
||||
• Restore archived files
|
||||
• View backup history
|
||||
• Manage backup schedules
|
||||
|
||||
Select an option or tell me what you need.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a backup management assistant.
|
||||
|
||||
Archive files older than specified days to server storage.
|
||||
Track all backup operations in log.xlsx.
|
||||
Support restore operations from archived files.
|
||||
Maintain MD5 checksums for integrity verification.
|
||||
END SYSTEM PROMPT
|
||||
146
backup.gbai/backup.gbkb/backup-guide.md
Normal file
146
backup.gbai/backup.gbkb/backup-guide.md
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
# Backup Management Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Backup Manager helps you protect your important files by archiving them to secure server storage. Regular backups ensure business continuity and data protection.
|
||||
|
||||
## Features
|
||||
|
||||
### Automated Backups
|
||||
- Schedule automatic backups at regular intervals
|
||||
- Archive files based on age criteria
|
||||
- Maintain backup rotation policies
|
||||
|
||||
### Manual Backups
|
||||
- On-demand backup of specific files or folders
|
||||
- Selective backup based on file types
|
||||
- Priority backup for critical data
|
||||
|
||||
### Restore Operations
|
||||
- Browse archived files by date
|
||||
- Restore individual files or entire folders
|
||||
- Preview files before restoration
|
||||
|
||||
## How to Use
|
||||
|
||||
### Running a Backup
|
||||
|
||||
To start a backup, you can:
|
||||
1. Say "Run backup now" or select the backup option
|
||||
2. Specify which files or folders to back up
|
||||
3. Confirm the backup operation
|
||||
|
||||
### Viewing Archived Files
|
||||
|
||||
To see your backup history:
|
||||
1. Say "View archived files" or "List backups"
|
||||
2. Browse by date or file name
|
||||
3. Select files to view details or restore
|
||||
|
||||
### Restoring Files
|
||||
|
||||
To restore from backup:
|
||||
1. Say "Restore a file" or select restore option
|
||||
2. Search for the file by name or date
|
||||
3. Confirm the restoration location
|
||||
|
||||
## Backup Best Practices
|
||||
|
||||
### Frequency Recommendations
|
||||
|
||||
| Data Type | Recommended Frequency |
|
||||
|-----------|----------------------|
|
||||
| Critical business data | Daily |
|
||||
| Documents and files | Weekly |
|
||||
| System configurations | Before changes |
|
||||
| Archives and logs | Monthly |
|
||||
|
||||
### What to Back Up
|
||||
|
||||
**Always include:**
|
||||
- Business documents
|
||||
- Customer data
|
||||
- Financial records
|
||||
- Configuration files
|
||||
- Email archives
|
||||
|
||||
**Consider excluding:**
|
||||
- Temporary files
|
||||
- Cache directories
|
||||
- Downloaded installers
|
||||
- Duplicate files
|
||||
|
||||
## Storage and Retention
|
||||
|
||||
### Retention Policies
|
||||
|
||||
- **Daily backups**: Kept for 7 days
|
||||
- **Weekly backups**: Kept for 4 weeks
|
||||
- **Monthly backups**: Kept for 12 months
|
||||
- **Annual backups**: Kept for 7 years
|
||||
|
||||
### Storage Locations
|
||||
|
||||
Backups are stored on:
|
||||
- Primary: Secure server storage
|
||||
- Secondary: Offsite replication (if configured)
|
||||
|
||||
## Data Integrity
|
||||
|
||||
### Verification
|
||||
|
||||
All backups include:
|
||||
- MD5 checksums for integrity verification
|
||||
- File count validation
|
||||
- Size comparison checks
|
||||
|
||||
### Monitoring
|
||||
|
||||
The system logs:
|
||||
- Backup start and completion times
|
||||
- Files included in each backup
|
||||
- Any errors or warnings
|
||||
- Storage utilization
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Backup fails to start:**
|
||||
- Check storage space availability
|
||||
- Verify file permissions
|
||||
- Ensure no files are locked
|
||||
|
||||
**Restore not finding files:**
|
||||
- Verify the backup date range
|
||||
- Check file name spelling
|
||||
- Ensure the backup completed successfully
|
||||
|
||||
**Slow backup performance:**
|
||||
- Reduce backup scope
|
||||
- Schedule during off-peak hours
|
||||
- Check network connectivity
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How long does a backup take?**
|
||||
A: Depends on data volume. Initial backups take longer; subsequent backups are incremental.
|
||||
|
||||
**Q: Can I backup while working?**
|
||||
A: Yes, the system handles file locking gracefully.
|
||||
|
||||
**Q: Where are backups stored?**
|
||||
A: On the configured server storage with optional offsite replication.
|
||||
|
||||
**Q: How do I know if backups are working?**
|
||||
A: Check backup status or ask "Backup status" to see recent backup logs.
|
||||
|
||||
**Q: Can I exclude certain files?**
|
||||
A: Yes, specify exclusion patterns when configuring backups.
|
||||
|
||||
## Support
|
||||
|
||||
For backup-related issues:
|
||||
- Check the backup logs for error details
|
||||
- Verify storage availability
|
||||
- Contact your system administrator
|
||||
236
bank.gbai/bank.gbkb/banking-services.md
Normal file
236
bank.gbai/bank.gbkb/banking-services.md
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
# Banking Services Guide
|
||||
|
||||
## Account Types
|
||||
|
||||
### Checking Account
|
||||
A checking account is designed for daily transactions and provides easy access to your funds.
|
||||
|
||||
**Features:**
|
||||
- Unlimited transactions
|
||||
- Debit card included
|
||||
- Online and mobile banking access
|
||||
- Bill pay services
|
||||
- Direct deposit available
|
||||
|
||||
**Requirements:**
|
||||
- Minimum opening deposit: $25
|
||||
- Valid government-issued ID
|
||||
- Social Security Number or Tax ID
|
||||
|
||||
### Savings Account
|
||||
A savings account helps you grow your money with interest while keeping it accessible.
|
||||
|
||||
**Features:**
|
||||
- Competitive interest rates
|
||||
- No monthly maintenance fee with minimum balance
|
||||
- Automatic transfers from checking
|
||||
- Up to 6 withdrawals per month
|
||||
|
||||
**Interest Rates:**
|
||||
- Standard Savings: 0.50% APY
|
||||
- High-Yield Savings: 2.50% APY (minimum $1,000 balance)
|
||||
|
||||
### Certificate of Deposit (CD)
|
||||
CDs offer higher interest rates in exchange for keeping your money deposited for a fixed term.
|
||||
|
||||
**Terms Available:**
|
||||
- 6 months: 3.00% APY
|
||||
- 12 months: 3.50% APY
|
||||
- 24 months: 4.00% APY
|
||||
- 60 months: 4.50% APY
|
||||
|
||||
**Early Withdrawal Penalty:** 90 days of interest
|
||||
|
||||
## Transfer Services
|
||||
|
||||
### Internal Transfers
|
||||
Move money between your accounts instantly at no cost.
|
||||
|
||||
**How to transfer:**
|
||||
1. Select source account
|
||||
2. Select destination account
|
||||
3. Enter amount
|
||||
4. Confirm transfer
|
||||
|
||||
### External Transfers (ACH)
|
||||
Transfer funds to accounts at other banks.
|
||||
|
||||
**Processing Times:**
|
||||
- Standard: 2-3 business days
|
||||
- Same-day ACH: Available for transfers initiated before 2 PM
|
||||
|
||||
**Limits:**
|
||||
- Daily limit: $10,000
|
||||
- Monthly limit: $50,000
|
||||
|
||||
### Wire Transfers
|
||||
For urgent or large transfers, domestic and international.
|
||||
|
||||
**Domestic Wires:**
|
||||
- Fee: $25
|
||||
- Processing: Same business day if before 4 PM
|
||||
|
||||
**International Wires:**
|
||||
- Fee: $45
|
||||
- Processing: 1-2 business days
|
||||
|
||||
### PIX (Brazil)
|
||||
Instant payment system available 24/7, including holidays.
|
||||
|
||||
**Features:**
|
||||
- Instant transfers
|
||||
- No fees for individuals
|
||||
- Available via CPF, phone, email, or random key
|
||||
- QR code payments supported
|
||||
|
||||
**Limits:**
|
||||
- Daytime (6 AM - 8 PM): R$ 20,000
|
||||
- Nighttime (8 PM - 6 AM): R$ 1,000 (adjustable)
|
||||
|
||||
## Card Services
|
||||
|
||||
### Debit Card
|
||||
Your debit card is linked directly to your checking account.
|
||||
|
||||
**Daily Limits:**
|
||||
- ATM withdrawal: $500
|
||||
- Point of sale purchases: $2,500
|
||||
|
||||
**Lost or Stolen Card:**
|
||||
Contact us immediately to block your card. A replacement will be sent within 5-7 business days.
|
||||
|
||||
### Credit Card
|
||||
Build credit and earn rewards with our credit cards.
|
||||
|
||||
**Card Options:**
|
||||
- Classic: No annual fee, 1% cashback
|
||||
- Gold: $95 annual fee, 2% cashback, travel insurance
|
||||
- Platinum: $195 annual fee, 3% cashback, concierge service
|
||||
|
||||
**Payment Due Date:** 25th of each month
|
||||
|
||||
### Card Blocking
|
||||
If your card is lost or stolen:
|
||||
1. Say "block my card" to this assistant
|
||||
2. Call 1-800-BANK-HELP
|
||||
3. Use the mobile app
|
||||
|
||||
Temporary blocks can be lifted at any time.
|
||||
|
||||
## Bill Pay
|
||||
|
||||
### One-Time Payments
|
||||
Pay any bill directly from your account.
|
||||
|
||||
**Supported Billers:**
|
||||
- Utilities
|
||||
- Credit cards
|
||||
- Loans
|
||||
- Insurance
|
||||
- Government agencies
|
||||
|
||||
### Recurring Payments
|
||||
Set up automatic payments for regular bills.
|
||||
|
||||
**Options:**
|
||||
- Fixed amount on specific date
|
||||
- Full statement balance
|
||||
- Minimum payment due
|
||||
|
||||
### Payment Processing
|
||||
- Electronic payments: 1-2 business days
|
||||
- Check payments: 5-7 business days
|
||||
|
||||
## Loans and Financing
|
||||
|
||||
### Personal Loans
|
||||
Unsecured loans for any purpose.
|
||||
|
||||
**Terms:** 12 to 60 months
|
||||
**Rates:** 8.99% - 18.99% APR
|
||||
**Amounts:** $1,000 - $50,000
|
||||
|
||||
### Home Loans
|
||||
Mortgages for purchasing or refinancing your home.
|
||||
|
||||
**Types Available:**
|
||||
- 30-year fixed
|
||||
- 15-year fixed
|
||||
- Adjustable rate (ARM)
|
||||
- FHA loans
|
||||
- VA loans
|
||||
|
||||
### Auto Loans
|
||||
Financing for new or used vehicles.
|
||||
|
||||
**New Cars:** Rates from 5.49% APR
|
||||
**Used Cars:** Rates from 6.49% APR
|
||||
**Terms:** 24 to 72 months
|
||||
|
||||
### Credit Line
|
||||
Flexible borrowing when you need it.
|
||||
|
||||
**Home Equity Line (HELOC):** Up to 85% of home value
|
||||
**Personal Line of Credit:** $5,000 - $25,000
|
||||
|
||||
## Investment Services
|
||||
|
||||
### Investment Options
|
||||
- Savings bonds
|
||||
- CDB (Certificado de Depósito Bancário)
|
||||
- LCI/LCA (tax-exempt real estate/agribusiness letters)
|
||||
- Investment funds
|
||||
- Retirement accounts (401k, IRA, Previdência)
|
||||
|
||||
### Retirement Planning
|
||||
Our advisors can help you plan for retirement with:
|
||||
- Portfolio analysis
|
||||
- Risk assessment
|
||||
- Tax-efficient strategies
|
||||
- Regular reviews
|
||||
|
||||
## Security Information
|
||||
|
||||
### Online Banking Security
|
||||
- 256-bit SSL encryption
|
||||
- Two-factor authentication
|
||||
- Automatic session timeout
|
||||
- Transaction alerts
|
||||
|
||||
### Fraud Protection
|
||||
- 24/7 fraud monitoring
|
||||
- Zero liability for unauthorized transactions
|
||||
- Instant transaction alerts
|
||||
- Secure card chip technology
|
||||
|
||||
### What We Will Never Ask
|
||||
- Full card number via chat or email
|
||||
- Your password or PIN
|
||||
- Security codes via unsolicited contact
|
||||
- Personal information via text message
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I check my balance?**
|
||||
A: Say "check my balance" or "what's my balance" to this assistant.
|
||||
|
||||
**Q: How do I dispute a transaction?**
|
||||
A: Contact us within 60 days of the transaction. Provide the date, amount, and merchant name.
|
||||
|
||||
**Q: What are your business hours?**
|
||||
A: Digital banking is available 24/7. Branch hours are Monday-Friday 9 AM - 5 PM, Saturday 9 AM - 1 PM.
|
||||
|
||||
**Q: How do I update my contact information?**
|
||||
A: You can update your address, phone, and email through online banking or by visiting a branch.
|
||||
|
||||
**Q: Is there a fee for using other banks' ATMs?**
|
||||
A: We charge $3 per transaction. The ATM owner may charge an additional fee.
|
||||
|
||||
## Contact Information
|
||||
|
||||
- **Customer Service:** 1-800-BANK-HELP
|
||||
- **Fraud Hotline:** 1-800-FRAUD-911
|
||||
- **International:** +1-555-123-4567
|
||||
- **Email:** support@generalbank.com
|
||||
|
||||
For immediate assistance, continue chatting with this assistant.
|
||||
53
bank.gbai/start.bas
Normal file
53
bank.gbai/start.bas
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
ADD TOOL "check-balance"
|
||||
ADD TOOL "transfer-money"
|
||||
ADD TOOL "pay-bill"
|
||||
ADD TOOL "card-services"
|
||||
ADD TOOL "loan-inquiry"
|
||||
ADD TOOL "investment-info"
|
||||
ADD TOOL "transaction-history"
|
||||
ADD TOOL "open-account"
|
||||
|
||||
ADD BOT "fraud-detector" WITH TRIGGER "suspicious, fraud, unauthorized, stolen, hack"
|
||||
ADD BOT "investment-advisor" WITH TRIGGER "invest, stocks, funds, portfolio, returns, CDB, LCI"
|
||||
ADD BOT "loan-specialist" WITH TRIGGER "loan, financing, credit, mortgage, empréstimo"
|
||||
ADD BOT "card-services" WITH TRIGGER "card, credit card, debit card, block card, limit"
|
||||
|
||||
USE KB "banking-faq"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "balance" AS "Check my balance"
|
||||
ADD SUGGESTION "transfer" AS "Make a transfer"
|
||||
ADD SUGGESTION "pix" AS "Send PIX"
|
||||
ADD SUGGESTION "bills" AS "Pay a bill"
|
||||
ADD SUGGESTION "card" AS "Card services"
|
||||
ADD SUGGESTION "history" AS "Transaction history"
|
||||
ADD SUGGESTION "invest" AS "Investment options"
|
||||
ADD SUGGESTION "loan" AS "Loan information"
|
||||
|
||||
SET CONTEXT "You are a professional banking assistant for General Bank. Help customers with accounts, transfers, payments, cards, loans, and investments. Always verify identity before sensitive operations. Be helpful and secure. Never ask for full card numbers or passwords in chat."
|
||||
|
||||
BEGIN TALK
|
||||
**General Bank** - Digital Banking Assistant
|
||||
|
||||
Welcome! I can help you with:
|
||||
• Account balance and statements
|
||||
• Transfers and PIX
|
||||
• Bill payments
|
||||
• Card services
|
||||
• Investments
|
||||
• Loans and financing
|
||||
|
||||
Select an option below or tell me what you need.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a secure banking assistant.
|
||||
|
||||
Security rules:
|
||||
- Never display full account numbers
|
||||
- Mask card numbers showing only last 4 digits
|
||||
- Require confirmation for transactions over $1000
|
||||
- Log all sensitive operations
|
||||
- Escalate fraud concerns immediately
|
||||
END SYSTEM PROMPT
|
||||
245
bi.gbai/README.md
Normal file
245
bi.gbai/README.md
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
# Business Intelligence Template (bi.gbai)
|
||||
|
||||
A General Bots template for automated business intelligence reporting and data visualization.
|
||||
|
||||
## Overview
|
||||
|
||||
The BI template provides scheduled analytics reporting with automatic chart generation and delivery. It's designed for organizations that need automated consumption reports, category analysis, and customer-specific insights.
|
||||
|
||||
## Features
|
||||
|
||||
- **Scheduled Reporting** - Automated report generation on configurable schedules
|
||||
- **Time-Series Charts** - Monthly consumption trends visualization
|
||||
- **Category Analysis** - Product category breakdown with donut charts
|
||||
- **Per-Customer Reports** - Individual customer consumption analysis
|
||||
- **Multi-Channel Delivery** - Send reports via chat, email, or messaging platforms
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
bi.gbai/
|
||||
├── README.md
|
||||
└── bi.gbai/
|
||||
├── bi-admin.bas # Administrative scheduled reports
|
||||
└── bi-user.bas # Per-customer report generation
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `bi-admin.bas` | Scheduled job for generating platform-wide analytics reports |
|
||||
| `bi-user.bas` | Loop through customers to generate individual consumption reports |
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure the template in your bot's `config.csv`:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `Schedule` | Cron expression for report timing | `1 * * * * *` |
|
||||
| `Data Source` | Table/view for billing data | `Orders` |
|
||||
|
||||
## Usage
|
||||
|
||||
### Administrative Reports
|
||||
|
||||
The `bi-admin.bas` script runs on a schedule and generates:
|
||||
|
||||
1. **Monthly Consumption Chart** - Time-series showing spending trends
|
||||
2. **Product Category Breakdown** - Donut chart of spending by category
|
||||
|
||||
```basic
|
||||
SET SCHEDULE "1 * * * * *"
|
||||
|
||||
billing = FIND "Orders"
|
||||
|
||||
' Monthly consumption
|
||||
data = SELECT SUM(UnitPrice * Quantity) as Value,
|
||||
MONTH(OrderDate)+'/'+YEAR(OrderDate)
|
||||
FROM billing
|
||||
GROUP BY MONTH(OrderDate), YEAR(OrderDate)
|
||||
|
||||
img = CHART "timeseries", data
|
||||
SEND FILE img, "Monthly Consumption"
|
||||
```
|
||||
|
||||
### Per-Customer Reports
|
||||
|
||||
The `bi-user.bas` script iterates through customers to generate personalized reports:
|
||||
|
||||
```basic
|
||||
customers = FIND "Customers"
|
||||
|
||||
FOR EACH c IN customers
|
||||
data = SELECT SUM(UnitPrice * Quantity) AS Value,
|
||||
MONTH(OrderDate)+'/'+YEAR(OrderDate)
|
||||
FROM billing
|
||||
JOIN Customers ON billing.CustomerID = Customers.CustomerID
|
||||
GROUP BY MONTH(OrderDate), YEAR(OrderDate)
|
||||
WHERE Customers.CustomerID = c.CustomerID
|
||||
|
||||
img = CHART "timeseries", data
|
||||
SEND FILE img, "Monthly Consumption"
|
||||
END FOR
|
||||
```
|
||||
|
||||
## Chart Types
|
||||
|
||||
The template supports various chart types:
|
||||
|
||||
| Type | Use Case |
|
||||
|------|----------|
|
||||
| `timeseries` | Trends over time (monthly, weekly, daily) |
|
||||
| `donut` | Category distribution |
|
||||
| `bar` | Comparative analysis |
|
||||
| `pie` | Percentage breakdowns |
|
||||
|
||||
## Data Requirements
|
||||
|
||||
### Orders Table Schema
|
||||
|
||||
The template expects a billing/orders data source with:
|
||||
|
||||
- `OrderDate` - Date of the transaction
|
||||
- `UnitPrice` - Price per unit
|
||||
- `Quantity` - Number of units
|
||||
- `ProductID` - Foreign key to products
|
||||
- `CustomerID` - Foreign key to customers
|
||||
|
||||
### Products Table Schema
|
||||
|
||||
- `ProductID` - Primary key
|
||||
- `CategoryID` - Foreign key to categories
|
||||
- `ProductName` - Product name
|
||||
|
||||
### Categories Table Schema
|
||||
|
||||
- `CategoryID` - Primary key
|
||||
- `CategoryName` - Category display name
|
||||
|
||||
## Example Output
|
||||
|
||||
### Monthly Consumption Report
|
||||
```
|
||||
📊 Monthly Consumption Report
|
||||
-----------------------------
|
||||
Generated: 2024-01-15 08:00
|
||||
|
||||
[Time Series Chart Image]
|
||||
|
||||
Total Revenue: $125,430
|
||||
Top Month: December ($18,500)
|
||||
Growth Rate: +12% MoM
|
||||
```
|
||||
|
||||
### Category Breakdown
|
||||
```
|
||||
📊 Product Category Distribution
|
||||
--------------------------------
|
||||
|
||||
[Donut Chart Image]
|
||||
|
||||
Electronics: 35%
|
||||
Clothing: 28%
|
||||
Home & Garden: 22%
|
||||
Other: 15%
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding New Reports
|
||||
|
||||
Create additional `.bas` files in the `bi.gbai` folder:
|
||||
|
||||
```basic
|
||||
' sales-by-region.bas
|
||||
SET SCHEDULE "0 9 * * 1" ' Every Monday at 9 AM
|
||||
|
||||
data = SELECT Region, SUM(Amount) as Total
|
||||
FROM Sales
|
||||
GROUP BY Region
|
||||
|
||||
img = CHART "bar", data
|
||||
SEND FILE img, "Weekly Regional Sales"
|
||||
```
|
||||
|
||||
### Customizing Delivery
|
||||
|
||||
Send reports to specific users or channels:
|
||||
|
||||
```basic
|
||||
' Send to specific user
|
||||
SEND FILE img TO "manager@company.com", "Weekly Report"
|
||||
|
||||
' Send to WhatsApp
|
||||
SEND FILE img TO "+1234567890", "Your monthly report"
|
||||
|
||||
' Send to team channel
|
||||
TALK TO "sales-team", img
|
||||
```
|
||||
|
||||
## Scheduling Options
|
||||
|
||||
| Schedule | Cron Expression | Description |
|
||||
|----------|-----------------|-------------|
|
||||
| Every minute | `1 * * * * *` | Testing/real-time |
|
||||
| Hourly | `0 0 * * * *` | Frequent updates |
|
||||
| Daily 8 AM | `0 0 8 * * *` | Morning reports |
|
||||
| Weekly Monday | `0 0 9 * * 1` | Weekly summaries |
|
||||
| Monthly 1st | `0 0 8 1 * *` | Monthly reports |
|
||||
|
||||
## Integration
|
||||
|
||||
### With CRM
|
||||
|
||||
```basic
|
||||
' Combine with CRM data
|
||||
opportunities = FIND "opportunities.csv"
|
||||
revenue = SELECT stage, SUM(amount) FROM opportunities GROUP BY stage
|
||||
|
||||
img = CHART "funnel", revenue
|
||||
SEND FILE img, "Sales Pipeline"
|
||||
```
|
||||
|
||||
### With ERP
|
||||
|
||||
```basic
|
||||
' Inventory analysis
|
||||
inventory = FIND "inventory.csv"
|
||||
low_stock = SELECT product, quantity FROM inventory WHERE quantity < reorder_level
|
||||
|
||||
img = CHART "bar", low_stock
|
||||
SEND FILE img, "Low Stock Alert"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Schedule appropriately** - Don't run heavy reports too frequently
|
||||
2. **Filter data** - Use date ranges to limit data volume
|
||||
3. **Cache results** - Store computed metrics for faster access
|
||||
4. **Log activities** - Track report generation for auditing
|
||||
5. **Handle errors** - Wrap queries in error handling
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Empty charts | Verify data source has records |
|
||||
| Schedule not running | Check cron syntax |
|
||||
| Slow reports | Add date filters, optimize queries |
|
||||
| Missing data | Verify JOIN conditions |
|
||||
|
||||
## Related Templates
|
||||
|
||||
- `analytics.gbai` - Platform analytics
|
||||
- `talk-to-data.gbai` - Natural language data queries
|
||||
- `crm.gbai` - CRM with built-in reporting
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - Part of General Bots Open Source Platform.
|
||||
|
||||
---
|
||||
|
||||
**Pragmatismo** - General Bots
|
||||
13
bi.gbai/bi.gbai/bi-admin.bas
Normal file
13
bi.gbai/bi.gbai/bi-admin.bas
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
SET SCHEDULE "1 * * * * *"
|
||||
|
||||
billing = FIND "Orders"
|
||||
|
||||
' Monthly consumption
|
||||
data = SELECT SUM(UnitPrice * Quantity) as Value, MONTH(OrderDate)+'/'+YEAR(OrderDate) from billing GROUP BY MONTH(OrderDate), YEAR(OrderDate)
|
||||
img = CHART "timeseries", data
|
||||
SEND FILE img, "Monthly Consumption"
|
||||
|
||||
' Product Category
|
||||
data = SELECT SUM(UnitPrice * Quantity) as Value, CategoryName from billing JOIN Products ON billing.ProductID = Products.ProductID JOIN Categories ON Products.CategoryID = Categories.CategoryID GROUP BY CategoryName
|
||||
img = CHART "donut", data
|
||||
SEND FILE img, "Product Category"
|
||||
13
bi.gbai/bi.gbai/bi-user.bas
Normal file
13
bi.gbai/bi.gbai/bi-user.bas
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
' Individual customer report generation
|
||||
|
||||
customers = FIND "Customers"
|
||||
|
||||
FOR EACH c IN customers
|
||||
data = SELECT SUM(UnitPrice * Quantity) AS Value, MONTH(OrderDate)+'/'+YEAR(OrderDate) FROM billing
|
||||
JOIN Customers ON billing.CustomerID = Customers.CustomerID
|
||||
GROUP BY MONTH(OrderDate), YEAR(OrderDate)
|
||||
WHERE Customers.CustomerID = c.CustomerID
|
||||
|
||||
img = CHART "timeseries", data
|
||||
SEND FILE img, "Monthly Consumption"
|
||||
END FOR
|
||||
118
bling.gbai/bling.gbdialog/README.md
Normal file
118
bling.gbai/bling.gbdialog/README.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Bling ERP Integration (.gbdialog)
|
||||
|
||||
This package provides complete integration with [Bling ERP](https://www.bling.com.br/) for data synchronization and conversational commerce.
|
||||
|
||||
## Scripts
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `start.bas` | Welcome message and system prompt configuration |
|
||||
| `tables.bas` | Database schema definitions for all synced entities |
|
||||
| `sync-erp.bas` | Main ERP synchronization (products, orders, contacts, vendors) |
|
||||
| `sync-accounts.bas` | Accounts payable and receivable synchronization |
|
||||
| `sync-inventory.bas` | Stock/inventory levels synchronization |
|
||||
| `sync-suppliers.bas` | Supplier/vendor data synchronization |
|
||||
| `add-stock.bas` | Manual stock adjustment tool |
|
||||
| `data-analysis.bas` | LLM-powered data analysis and reporting |
|
||||
| `refresh-llm.bas` | Scheduled LLM context refresh |
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure the integration in `bling.gbot/config.csv`:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `param-blingClientID` | Bling API Client ID |
|
||||
| `param-blingClientSecret` | Bling API Client Secret |
|
||||
| `param-blingHost` | Bling API base URL |
|
||||
| `param-host` | API endpoint (default: `https://api.bling.com.br/Api/v3`) |
|
||||
| `param-limit` | Records per page for API calls |
|
||||
| `param-pages` | Maximum pages to sync |
|
||||
| `param-admin1` | Primary admin email for notifications |
|
||||
| `param-admin2` | Secondary admin email for notifications |
|
||||
|
||||
## Synchronized Entities
|
||||
|
||||
### Products (`maria.Produtos`)
|
||||
- Product details, SKU, pricing
|
||||
- Product variations and hierarchy
|
||||
- Product images (`maria.ProdutoImagem`)
|
||||
|
||||
### Orders (`maria.Pedidos`)
|
||||
- Sales orders with line items (`maria.PedidosItem`)
|
||||
- Payment parcels (`maria.Parcela`)
|
||||
|
||||
### Contacts (`maria.Contatos`)
|
||||
- Customers and suppliers
|
||||
- Address and billing information
|
||||
|
||||
### Vendors (`maria.Vendedores`)
|
||||
- Sales representatives
|
||||
- Commission and discount limits
|
||||
|
||||
### Financial
|
||||
- Accounts Receivable (`maria.ContasAReceber`)
|
||||
- Accounts Payable (`maria.ContasAPagar`)
|
||||
- Payment Methods (`maria.FormaDePagamento`)
|
||||
- Revenue Categories (`maria.CategoriaReceita`)
|
||||
|
||||
### Inventory
|
||||
- Stock by Warehouse (`maria.Depositos`)
|
||||
- Product Suppliers (`maria.ProdutoFornecedor`)
|
||||
- Price History (`maria.HistoricoPreco`)
|
||||
|
||||
## Scheduled Jobs
|
||||
|
||||
The following schedules are configured:
|
||||
|
||||
| Job | Schedule | Description |
|
||||
|-----|----------|-------------|
|
||||
| `sync-erp.bas` | Daily at 22:30 | Full ERP synchronization |
|
||||
| `sync-accounts.bas` | Every 2 days at midnight | Financial accounts sync |
|
||||
| `sync-inventory.bas` | Daily at 23:30 | Stock levels update |
|
||||
| `refresh-llm.bas` | Daily at 21:00 | Refresh LLM context |
|
||||
|
||||
## Data Analysis
|
||||
|
||||
The `data-analysis.bas` script enables natural language queries against synced data:
|
||||
|
||||
**Example queries:**
|
||||
- "Which products have excess stock that can be transferred?"
|
||||
- "What are the top 10 best-selling products?"
|
||||
- "What is the average ticket for each store?"
|
||||
- "Which products need restocking?"
|
||||
|
||||
## Usage
|
||||
|
||||
### Manual Stock Adjustment
|
||||
|
||||
Vendors can adjust stock via conversation:
|
||||
|
||||
```basic
|
||||
REM User provides SKU and quantity
|
||||
REM Stock is updated in Bling and local database
|
||||
```
|
||||
|
||||
### Running Sync Manually
|
||||
|
||||
```basic
|
||||
RUN "sync-erp.bas"
|
||||
RUN "sync-accounts.bas"
|
||||
RUN "sync-inventory.bas"
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
All API calls use the Bling v3 REST API with pagination support:
|
||||
|
||||
- Products: `GET /produtos`
|
||||
- Orders: `GET /pedidos/vendas`
|
||||
- Contacts: `GET /contatos`
|
||||
- Inventory: `GET /estoques/saldos`
|
||||
- Accounts: `GET /contas/receber`, `GET /contas/pagar`
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Bling API Documentation](https://developer.bling.com.br/)
|
||||
- [General Bots BASIC Reference](../../docs/src/chapter-06-gbdialog/README.md)
|
||||
- [Template Guide](../../README.md)
|
||||
33
bling.gbai/bling.gbdialog/add-stock.bas
Normal file
33
bling.gbai/bling.gbdialog/add-stock.bas
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
PARAM sku AS STRING LIKE "ABC123" DESCRIPTION "Product SKU code to update stock"
|
||||
PARAM qtd AS INTEGER LIKE 10 DESCRIPTION "Quantity to add to stock"
|
||||
|
||||
DESCRIPTION "Add stock quantity for a product by SKU"
|
||||
|
||||
person = FIND "People.xlsx", "id=" + mobile
|
||||
vendor = FIND "maria.Vendedores", "id=" + person.erpId
|
||||
|
||||
TALK "Olá " + vendor.Contato_Nome + "!"
|
||||
|
||||
produto = FIND "maria.Produtos", "sku=" + sku
|
||||
|
||||
IF NOT produto THEN
|
||||
TALK "Produto não encontrado."
|
||||
RETURN
|
||||
END IF
|
||||
|
||||
WITH estoque
|
||||
produto = { id: produto.Id }
|
||||
deposito = { id: person.deposito_Id }
|
||||
preco = produto.Preco
|
||||
operacao = "B"
|
||||
quantidade = qtd
|
||||
observacoes = "Acréscimo de estoque."
|
||||
END WITH
|
||||
|
||||
rec = POST host + "/estoques", estoque
|
||||
|
||||
TALK "Estoque atualizado."
|
||||
TALK TO admin1, "Estoque do ${sku} atualizado com ${qtd}."
|
||||
TALK TO admin2, "Estoque do ${sku} atualizado com ${qtd}."
|
||||
|
||||
RETURN rec
|
||||
53
bling.gbai/bling.gbdialog/data-analysis.bas
Normal file
53
bling.gbai/bling.gbdialog/data-analysis.bas
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
ALLOW ROLE "analiseDados"
|
||||
|
||||
ADD TOOL "sync-erp"
|
||||
ADD TOOL "sync-inventory"
|
||||
ADD TOOL "refresh-llm"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "estoque" AS "Produtos com estoque excessivo"
|
||||
ADD SUGGESTION "vendas" AS "Top 10 produtos vendidos"
|
||||
ADD SUGGESTION "ticket" AS "Ticket médio por loja"
|
||||
ADD SUGGESTION "critico" AS "Estoque crítico"
|
||||
ADD SUGGESTION "transferir" AS "Sugestão de transferência"
|
||||
ADD SUGGESTION "compra" AS "Sugestão de compra"
|
||||
|
||||
SET CONTEXT "As lojas B, L e R estão identificadas no final dos nomes das colunas da tabela de Análise de Compras. Dicionário de dados AnaliseCompras.qtEstoqueL: Descrição quantidade do Leblon. AnaliseCompras.qtEstoqueB: Descrição quantidade da Barra AnaliseCompras.qtEstoqueR: Descrição quantidade do Rio Sul. Com base no comportamento de compra registrado, analise os dados fornecidos para identificar oportunidades de otimização de estoque. Aplique regras básicas de transferência de produtos entre as lojas, considerando a necessidade de balanceamento de inventário. Retorne um relatório das 10 ações mais críticas, detalhe a movimentação sugerida para cada produto. Deve indicar a loja de origem, a loja de destino e o motivo da transferência. A análise deve ser objetiva e pragmática, focando na melhoria da disponibilidade de produtos nas lojas. Sempre use LIKE %% para comparar nomes. IMPORTANTE: Compare sempre com a função LOWER ao filtrar valores, em ambos os operandos de texto em SQL, para ignorar case, exemplo WHERE LOWER(loja.nome) LIKE LOWER(%Leblon%)."
|
||||
|
||||
SET ANSWER MODE "sql"
|
||||
|
||||
BEGIN TALK
|
||||
**BlingBot - Análise de Dados**
|
||||
|
||||
Exemplos de perguntas:
|
||||
|
||||
• Produtos com estoque excessivo para transferência
|
||||
• Top 10 produtos vendidos em {loja} no {período}
|
||||
• Ticket médio da loja {nome}
|
||||
• Estoque disponível do produto {nome} na loja {loja}
|
||||
• Produtos para transferir de {origem} para {destino}
|
||||
• Estoque crítico na loja {nome}
|
||||
• Sugestão de compra para fornecedor {nome}
|
||||
• Pedidos por dia na loja {nome}
|
||||
• Total de produtos ativos no sistema
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a data analyst for retail inventory management using Bling ERP.
|
||||
|
||||
Data available:
|
||||
- AnaliseCompras table with stock by store (B=Barra, L=Leblon, R=Rio Sul)
|
||||
- Products, Orders, Suppliers, Inventory tables
|
||||
|
||||
Analysis capabilities:
|
||||
- Stock optimization and transfer suggestions
|
||||
- Sales performance by store and period
|
||||
- Average ticket calculation
|
||||
- Critical stock alerts
|
||||
- Purchase recommendations
|
||||
|
||||
Always use LOWER() for text comparisons in SQL.
|
||||
Use LIKE with %% for partial matches.
|
||||
Return actionable insights with specific quantities and locations.
|
||||
END SYSTEM PROMPT
|
||||
7
bling.gbai/bling.gbdialog/refresh-llm.bas
Normal file
7
bling.gbai/bling.gbdialog/refresh-llm.bas
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
SET SCHEDULE "0 0 21 * * *"
|
||||
|
||||
DESCRIPTION "Refresh data analysis context for LLM"
|
||||
|
||||
REFRESH "data-analysis"
|
||||
|
||||
TALK "Data analysis context refreshed."
|
||||
36
bling.gbai/bling.gbdialog/start.bas
Normal file
36
bling.gbai/bling.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
ADD TOOL "add-stock"
|
||||
ADD TOOL "sync-erp"
|
||||
ADD TOOL "sync-inventory"
|
||||
ADD TOOL "sync-accounts"
|
||||
ADD TOOL "sync-suppliers"
|
||||
ADD TOOL "data-analysis"
|
||||
ADD TOOL "refresh-llm"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "estoque" AS "Consultar estoque"
|
||||
ADD SUGGESTION "pedido" AS "Fazer pedido"
|
||||
ADD SUGGESTION "sync" AS "Sincronizar ERP"
|
||||
ADD SUGGESTION "analise" AS "Análise de dados"
|
||||
|
||||
BEGIN TALK
|
||||
**BlingBot** - Assistente ERP
|
||||
|
||||
Olá! Posso ajudar com:
|
||||
• 📦 Consulta de estoque
|
||||
• 🛒 Pedidos e vendas
|
||||
• 🔄 Sincronização com Bling
|
||||
• 📊 Análise de dados
|
||||
|
||||
Qual o seu pedido?
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
Você é um assistente de loja integrado ao Bling ERP.
|
||||
|
||||
Ao receber pedido, ofereça opções de cor e tamanho do JSON de produtos.
|
||||
Retorne JSON do pedido com itens e nome do cliente.
|
||||
Mantenha itensPedido com apenas um item por vez.
|
||||
Use o mesmo id do JSON de produtos para correlação.
|
||||
ItensAcompanhamento contém itens adicionais do pedido (ex: Quadro com Caixa de Giz).
|
||||
END SYSTEM PROMPT
|
||||
78
bling.gbai/bling.gbdialog/sync-accounts.bas
Normal file
78
bling.gbai/bling.gbdialog/sync-accounts.bas
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
SET SCHEDULE "0 0 0 */2 * *"
|
||||
|
||||
pageVariable = "pagina"
|
||||
limitVariable = "limite"
|
||||
syncLimit = 100
|
||||
|
||||
' Contas a Receber
|
||||
SEND EMAIL admin, "Syncing Accounts Receivable..."
|
||||
|
||||
page = 1
|
||||
totalReceber = 0
|
||||
|
||||
DO WHILE page > 0 AND page <= pages
|
||||
url = host + "/contas/receber?" + pageVariable + "=" + page + "&" + limitVariable + "=" + syncLimit
|
||||
res = GET url
|
||||
WAIT 0.33
|
||||
|
||||
IF res.data THEN
|
||||
items = res.data
|
||||
itemCount = UBOUND(items)
|
||||
|
||||
IF itemCount > 0 THEN
|
||||
MERGE "maria.ContasAReceber" WITH items BY "Id"
|
||||
totalReceber = totalReceber + itemCount
|
||||
page = page + 1
|
||||
|
||||
IF itemCount < syncLimit THEN
|
||||
page = 0
|
||||
END IF
|
||||
ELSE
|
||||
page = 0
|
||||
END IF
|
||||
ELSE
|
||||
page = 0
|
||||
END IF
|
||||
|
||||
res = null
|
||||
items = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Accounts Receivable: " + totalReceber + " records."
|
||||
|
||||
' Contas a Pagar
|
||||
SEND EMAIL admin, "Syncing Accounts Payable..."
|
||||
|
||||
page = 1
|
||||
totalPagar = 0
|
||||
|
||||
DO WHILE page > 0 AND page <= pages
|
||||
url = host + "/contas/pagar?" + pageVariable + "=" + page + "&" + limitVariable + "=" + syncLimit
|
||||
res = GET url
|
||||
WAIT 0.33
|
||||
|
||||
IF res.data THEN
|
||||
items = res.data
|
||||
itemCount = UBOUND(items)
|
||||
|
||||
IF itemCount > 0 THEN
|
||||
MERGE "maria.ContasAPagar" WITH items BY "Id"
|
||||
totalPagar = totalPagar + itemCount
|
||||
page = page + 1
|
||||
|
||||
IF itemCount < syncLimit THEN
|
||||
page = 0
|
||||
END IF
|
||||
ELSE
|
||||
page = 0
|
||||
END IF
|
||||
ELSE
|
||||
page = 0
|
||||
END IF
|
||||
|
||||
res = null
|
||||
items = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Accounts Payable: " + totalPagar + " records."
|
||||
SEND EMAIL admin, "Accounts sync completed. Total: " + (totalReceber + totalPagar) + " records."
|
||||
318
bling.gbai/bling.gbdialog/sync-erp.bas
Normal file
318
bling.gbai/bling.gbdialog/sync-erp.bas
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
SET SCHEDULE "0 30 22 * * *"
|
||||
|
||||
daysToSync = -7
|
||||
ontem = DATEADD today, "days", daysToSync
|
||||
ontem = FORMAT ontem, "yyyy-MM-dd"
|
||||
tomorrow = DATEADD today, "days", 1
|
||||
tomorrow = FORMAT tomorrow, "yyyy-MM-dd"
|
||||
dateFilter = "&dataAlteracaoInicial=${ontem}&dataAlteracaoFinal=${tomorrow}"
|
||||
|
||||
SEND EMAIL admin, "Sync: ${ontem} to ${tomorrow} started..."
|
||||
|
||||
' Produtos
|
||||
i = 1
|
||||
SEND EMAIL admin, "Syncing Products..."
|
||||
|
||||
DO WHILE i > 0 AND i < pages
|
||||
res = GET host + "/produtos?pagina=${i}&criterio=5&tipo=P&limite=${limit}${dateFilter}"
|
||||
WAIT 0.33
|
||||
list = res.data
|
||||
res = null
|
||||
|
||||
prd1 = ""
|
||||
j = 0
|
||||
k = 0
|
||||
items = NEW ARRAY
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
produto_id = list[j].id
|
||||
res = GET host + "/produtos/${produto_id}"
|
||||
WAIT 0.33
|
||||
produto = res.data
|
||||
res = null
|
||||
|
||||
IF produto.codigo && produto.codigo.trim().length THEN
|
||||
prd1 = prd1 + "&idsProdutos%5B%5D=" + list[j].id
|
||||
items[k] = produto
|
||||
produto.sku = items[k].codigo
|
||||
|
||||
IF produto.variacoes.length > 0 THEN
|
||||
produto.hierarquia = "p"
|
||||
ELSE
|
||||
produto.hierarquia = "s"
|
||||
END IF
|
||||
|
||||
produtoDB = FIND "maria.Produtos", "sku=" + produto.codigo
|
||||
IF produtoDB THEN
|
||||
IF produtoDB.preco <> produto.preco THEN
|
||||
hist = NEW OBJECT
|
||||
hist.sku = produto.sku
|
||||
hist.precoAntigo = produtoDB.preco
|
||||
hist.precoAtual = produto.preco
|
||||
hist.produto_id = produto.id
|
||||
hist.dataModificado = FORMAT today, "yyyy-MM-dd"
|
||||
SAVE "maria.HistoricoPreco", hist
|
||||
hist = null
|
||||
END IF
|
||||
END IF
|
||||
k = k + 1
|
||||
END IF
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
list = null
|
||||
list = items
|
||||
|
||||
MERGE "maria.Produtos" WITH list BY "Id"
|
||||
list = items
|
||||
|
||||
j = 0
|
||||
DO WHILE j < ubound(list)
|
||||
listV = list[j].variacoes
|
||||
IF listV THEN
|
||||
k = 0
|
||||
prd2 = ""
|
||||
DO WHILE k < ubound(listV)
|
||||
IF listV[k].codigo && listV[k].codigo.trim().length THEN
|
||||
listV[k].skuPai = list[j].sku
|
||||
listV[k].sku = listV[k].codigo
|
||||
listV[k].hierarquia = "f"
|
||||
k = k + 1
|
||||
ELSE
|
||||
listV.splice(k, 1)
|
||||
END IF
|
||||
LOOP
|
||||
|
||||
k = 0
|
||||
DO WHILE k < ubound(listV)
|
||||
listV[k].hierarquia = 'f'
|
||||
DELETE "maria.ProdutoImagem", "sku=" + listV[k].sku
|
||||
|
||||
images = listV[k]?.midia?.imagens?.externas
|
||||
l = 0
|
||||
DO WHILE l < ubound(images)
|
||||
images[l].ordinal = k
|
||||
images[l].sku = listV[k].sku
|
||||
images[l].id = random()
|
||||
l = l + 1
|
||||
LOOP
|
||||
SAVE "maria.ProdutoImagem", images
|
||||
images = null
|
||||
k = k + 1
|
||||
LOOP
|
||||
|
||||
MERGE "maria.Produtos" WITH listV BY "Id"
|
||||
END IF
|
||||
listV = null
|
||||
|
||||
DELETE "maria.ProdutoImagem", "sku=" + list[j].sku
|
||||
k = 0
|
||||
images = list[j].midia?.imagens?.externas
|
||||
DO WHILE k < ubound(images)
|
||||
images[k].ordinal = k
|
||||
images[k].sku = list[j].sku
|
||||
images[k].id = random()
|
||||
k = k + 1
|
||||
LOOP
|
||||
SAVE "maria.ProdutoImagem", images
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
i = i + 1
|
||||
IF list?.length < limit THEN
|
||||
i = 0
|
||||
END IF
|
||||
list = null
|
||||
res = null
|
||||
items = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Products completed."
|
||||
RESET REPORT
|
||||
|
||||
' Pedidos
|
||||
SEND EMAIL admin, "Syncing Orders..."
|
||||
i = 1
|
||||
|
||||
DO WHILE i > 0 AND i < pages
|
||||
res = GET host + "/pedidos/vendas?pagina=${i}&limite=${limit}${dateFilter}"
|
||||
list = res.data
|
||||
res = null
|
||||
|
||||
j = 0
|
||||
fullList = []
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
pedido_id = list[j].id
|
||||
res = GET host + "/pedidos/vendas/${pedido_id}"
|
||||
items = res.data.itens
|
||||
|
||||
k = 0
|
||||
DO WHILE k < ubound(items)
|
||||
items[k].pedido_id = pedido_id
|
||||
items[k].sku = items[k].codigo
|
||||
items[k].numero = list[j].numero
|
||||
items[k].custo = items[k].valor / 2
|
||||
k = k + 1
|
||||
LOOP
|
||||
MERGE "maria.PedidosItem" WITH items BY "Id"
|
||||
|
||||
items = res.data.parcelas
|
||||
k = 0
|
||||
DO WHILE k < ubound(items)
|
||||
items[k].pedido_id = pedido_id
|
||||
k = k + 1
|
||||
LOOP
|
||||
MERGE "maria.Parcela" WITH items BY "Id"
|
||||
|
||||
fullList[j] = res.data
|
||||
res = null
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
MERGE "maria.Pedidos" WITH fullList BY "Id"
|
||||
i = i + 1
|
||||
IF list?.length < limit THEN
|
||||
i = 0
|
||||
END IF
|
||||
list = null
|
||||
res = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Orders completed."
|
||||
|
||||
' Common entities
|
||||
pageVariable = "pagina"
|
||||
limitVariable = "limite"
|
||||
syncLimit = 100
|
||||
|
||||
' CategoriaReceita
|
||||
SEND EMAIL admin, "Syncing CategoriaReceita..."
|
||||
syncPage = 1
|
||||
totalCategoria = 0
|
||||
|
||||
DO WHILE syncPage > 0 AND syncPage <= pages
|
||||
syncUrl = host + "/categorias/receitas-despesas?" + pageVariable + "=" + syncPage + "&" + limitVariable + "=" + syncLimit
|
||||
syncRes = GET syncUrl
|
||||
WAIT 0.33
|
||||
|
||||
IF syncRes.data THEN
|
||||
syncItems = syncRes.data
|
||||
syncCount = UBOUND(syncItems)
|
||||
|
||||
IF syncCount > 0 THEN
|
||||
MERGE "maria.CategoriaReceita" WITH syncItems BY "Id"
|
||||
totalCategoria = totalCategoria + syncCount
|
||||
syncPage = syncPage + 1
|
||||
|
||||
IF syncCount < syncLimit THEN
|
||||
syncPage = 0
|
||||
END IF
|
||||
ELSE
|
||||
syncPage = 0
|
||||
END IF
|
||||
ELSE
|
||||
syncPage = 0
|
||||
END IF
|
||||
|
||||
syncRes = null
|
||||
syncItems = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "CategoriaReceita: " + totalCategoria + " records."
|
||||
|
||||
' FormaDePagamento
|
||||
SEND EMAIL admin, "Syncing Payment Methods..."
|
||||
syncPage = 1
|
||||
totalForma = 0
|
||||
|
||||
DO WHILE syncPage > 0 AND syncPage <= pages
|
||||
syncUrl = host + "/formas-pagamentos?" + pageVariable + "=" + syncPage + "&" + limitVariable + "=" + syncLimit
|
||||
syncRes = GET syncUrl
|
||||
WAIT 0.33
|
||||
|
||||
IF syncRes.data THEN
|
||||
syncItems = syncRes.data
|
||||
syncCount = UBOUND(syncItems)
|
||||
|
||||
IF syncCount > 0 THEN
|
||||
MERGE "maria.FormaDePagamento" WITH syncItems BY "Id"
|
||||
totalForma = totalForma + syncCount
|
||||
syncPage = syncPage + 1
|
||||
|
||||
IF syncCount < syncLimit THEN
|
||||
syncPage = 0
|
||||
END IF
|
||||
ELSE
|
||||
syncPage = 0
|
||||
END IF
|
||||
ELSE
|
||||
syncPage = 0
|
||||
END IF
|
||||
|
||||
syncRes = null
|
||||
syncItems = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Payment Methods: " + totalForma + " records."
|
||||
|
||||
' Contatos
|
||||
SEND EMAIL admin, "Syncing Contacts..."
|
||||
i = 1
|
||||
|
||||
DO WHILE i > 0 AND i < pages
|
||||
res = GET host + "/contatos?pagina=${i}&limite=${limit}${dateFilter}"
|
||||
list = res.data
|
||||
|
||||
j = 0
|
||||
items = NEW ARRAY
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
contato_id = list[j].id
|
||||
res = GET host + "/contatos/${contato_id}"
|
||||
items[j] = res.data
|
||||
WAIT 0.33
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
MERGE "maria.Contatos" WITH items BY "Id"
|
||||
i = i + 1
|
||||
IF list?.length < limit THEN
|
||||
i = 0
|
||||
END IF
|
||||
list = null
|
||||
res = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Contacts completed."
|
||||
|
||||
' Vendedores
|
||||
SEND EMAIL admin, "Syncing Sellers..."
|
||||
i = 1
|
||||
|
||||
DO WHILE i > 0 AND i < pages
|
||||
res = GET host + "/vendedores?pagina=${i}&situacaoContato=T&limite=${limit}${dateFilter}"
|
||||
list = res.data
|
||||
|
||||
j = 0
|
||||
items = NEW ARRAY
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
vendedor_id = list[j].id
|
||||
res = GET host + "/vendedores/${vendedor_id}"
|
||||
items[j] = res.data
|
||||
WAIT 0.33
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
MERGE "maria.Vendedores" WITH items BY "Id"
|
||||
i = i + 1
|
||||
IF list?.length < limit THEN
|
||||
i = 0
|
||||
END IF
|
||||
list = null
|
||||
res = null
|
||||
LOOP
|
||||
|
||||
SEND EMAIL admin, "Sellers completed."
|
||||
SEND EMAIL admin, "ERP sync completed."
|
||||
60
bling.gbai/bling.gbdialog/sync-inventory.bas
Normal file
60
bling.gbai/bling.gbdialog/sync-inventory.bas
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
SET SCHEDULE "0 30 23 * * *"
|
||||
|
||||
SEND EMAIL admin, "Inventory sync started..."
|
||||
|
||||
fullList = FIND "maria.Produtos"
|
||||
|
||||
chunkSize = 100
|
||||
startIndex = 0
|
||||
|
||||
DO WHILE startIndex < ubound(fullList)
|
||||
list = mid(fullList, startIndex, chunkSize)
|
||||
prd1 = ""
|
||||
j = 0
|
||||
items = NEW ARRAY
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
produto_id = list[j].id
|
||||
prd1 = prd1 + "&idsProdutos%5B%5D=" + produto_id
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
list = null
|
||||
|
||||
IF j > 0 THEN
|
||||
res = GET host + "/estoques/saldos?${prd1}"
|
||||
WAIT 0.33
|
||||
items = res.data
|
||||
res = null
|
||||
|
||||
k = 0
|
||||
DO WHILE k < ubound(items)
|
||||
depositos = items[k].depositos
|
||||
pSku = FIND "maria.Produtos", "id=${items[k].produto.id}"
|
||||
|
||||
IF pSku THEN
|
||||
prdSku = pSku.sku
|
||||
DELETE "maria.Depositos", "Sku=" + prdSku
|
||||
|
||||
l = 0
|
||||
DO WHILE l < ubound(depositos)
|
||||
depositos[l].sku = prdSku
|
||||
l = l + 1
|
||||
LOOP
|
||||
|
||||
SAVE "maria.Depositos", depositos
|
||||
depositos = null
|
||||
END IF
|
||||
|
||||
pSku = null
|
||||
k = k + 1
|
||||
LOOP
|
||||
items = null
|
||||
END IF
|
||||
|
||||
startIndex = startIndex + chunkSize
|
||||
items = null
|
||||
LOOP
|
||||
|
||||
fullList = null
|
||||
SEND EMAIL admin, "Inventory sync completed."
|
||||
62
bling.gbai/bling.gbdialog/sync-suppliers.bas
Normal file
62
bling.gbai/bling.gbdialog/sync-suppliers.bas
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
SET SCHEDULE "0 0 22 * * *"
|
||||
|
||||
DESCRIPTION "Sync product suppliers from Bling ERP to local database"
|
||||
|
||||
SEND EMAIL admin, "Suppliers sync started..."
|
||||
|
||||
FUNCTION SyncProdutoFornecedor(idProduto)
|
||||
DELETE "maria.ProdutoFornecedor", "Produto_id=" + idProduto
|
||||
|
||||
i1 = 1
|
||||
DO WHILE i1 > 0 AND i1 < pages
|
||||
res = GET host + "/produtos/fornecedores?pagina=${i1}&limite=${limit}&idProduto=${idProduto}"
|
||||
list1 = res.data
|
||||
res = null
|
||||
WAIT 0.33
|
||||
|
||||
j1 = 0
|
||||
items1 = NEW ARRAY
|
||||
|
||||
DO WHILE j1 < ubound(list1)
|
||||
produtoFornecedor_id = list1[j1].id
|
||||
res = GET host + "/produtos/fornecedores/${produtoFornecedor_id}"
|
||||
items1[j1] = res.data
|
||||
res = null
|
||||
WAIT 0.33
|
||||
j1 = j1 + 1
|
||||
LOOP
|
||||
|
||||
SAVE "maria.ProdutoFornecedor", items1
|
||||
items1 = null
|
||||
i1 = i1 + 1
|
||||
|
||||
IF list1?.length < limit THEN
|
||||
i1 = 0
|
||||
END IF
|
||||
res = null
|
||||
list1 = null
|
||||
LOOP
|
||||
END FUNCTION
|
||||
|
||||
fullList = FIND "maria.Produtos"
|
||||
|
||||
chunkSize = 100
|
||||
startIndex = 0
|
||||
|
||||
DO WHILE startIndex < ubound(fullList)
|
||||
list = mid(fullList, startIndex, chunkSize)
|
||||
|
||||
j = 0
|
||||
|
||||
DO WHILE j < ubound(list)
|
||||
produto_id = list[j].id
|
||||
CALL SyncProdutoFornecedor(produto_id)
|
||||
j = j + 1
|
||||
LOOP
|
||||
|
||||
list = null
|
||||
startIndex = startIndex + chunkSize
|
||||
LOOP
|
||||
|
||||
fullList = null
|
||||
SEND EMAIL admin, "Suppliers sync completed."
|
||||
284
bling.gbai/bling.gbdialog/tables.bas
Normal file
284
bling.gbai/bling.gbdialog/tables.bas
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
TABLE Contatos ON maria
|
||||
Id number key
|
||||
Nome string(150)
|
||||
Codigo string(50)
|
||||
Situacao string(5)
|
||||
NumeroDocumento string(25)
|
||||
Telefone string(20)
|
||||
Celular string(20)
|
||||
Fantasia string(150)
|
||||
Tipo string(5)
|
||||
IndicadorIe string(5)
|
||||
Ie string(22)
|
||||
Rg string(22)
|
||||
OrgaoEmissor string(22)
|
||||
Email string(50)
|
||||
Endereco_geral_endereco string(100)
|
||||
Endereco_geral_cep string(10)
|
||||
Endereco_geral_bairro string(50)
|
||||
Endereco_geral_municipio string(50)
|
||||
Endereco_geral_uf string(5)
|
||||
Endereco_geral_numero string(15)
|
||||
Endereco_geral_complemento string(50)
|
||||
Cobranca_endereco string(100)
|
||||
Cobranca_cep string(10)
|
||||
Cobranca_bairro string(50)
|
||||
Cobranca_municipio string(50)
|
||||
Cobranca_uf string(5)
|
||||
Cobranca_numero string(15)
|
||||
Cobranca_complemento string(50)
|
||||
Vendedor_id number
|
||||
DadosAdicionais_dataNascimento date
|
||||
DadosAdicionais_sexo string(5)
|
||||
DadosAdicionais_naturalidade string(25)
|
||||
Financeiro_limiteCredito double
|
||||
Financeiro_condicaoPagamento string(20)
|
||||
Financeiro_categoria_id number
|
||||
Pais_nome string(100)
|
||||
END TABLE
|
||||
|
||||
TABLE Pedidos ON maria
|
||||
Id number key
|
||||
Numero integer
|
||||
NumeroLoja string(15)
|
||||
Data date
|
||||
DataSaida date
|
||||
DataPrevista date
|
||||
TotalProdutos double
|
||||
Desconto_valor double
|
||||
Desconto_unidade string(15)
|
||||
Contato_id number
|
||||
Total double
|
||||
Contato_nome string(150)
|
||||
Contato_tipoPessoa string(1)
|
||||
Contato_numeroDocumento string(20)
|
||||
Situacao_id integer
|
||||
Situacao_valor double
|
||||
Loja_id integer
|
||||
Vendedor_id number
|
||||
NotaFiscal_id number
|
||||
END TABLE
|
||||
|
||||
TABLE PedidosItem ON maria
|
||||
Id number key
|
||||
Numero integer
|
||||
Sku string(20)
|
||||
Unidade string(8)
|
||||
Quantidade integer
|
||||
Desconto double
|
||||
Valor double
|
||||
Custo double
|
||||
AliquotaIPI double
|
||||
Descricao string(255)
|
||||
DescricaoDetalhada string(250)
|
||||
Produto_id number
|
||||
Pedido_id number
|
||||
END TABLE
|
||||
|
||||
TABLE ProdutoImagem ON maria
|
||||
Id number key
|
||||
Ordinal number
|
||||
Sku string(20)
|
||||
Link string(250)
|
||||
END TABLE
|
||||
|
||||
TABLE Produtos ON maria
|
||||
Id number key
|
||||
Nome string(150)
|
||||
Sku string(20)
|
||||
SkuPai string(20)
|
||||
Preco double
|
||||
Tipo string(1)
|
||||
Situacao string(1)
|
||||
Formato string(1)
|
||||
Hierarquia string(1)
|
||||
DescricaoCurta string(4000)
|
||||
DataValidade date
|
||||
Unidade string(5)
|
||||
PesoLiquido double
|
||||
PesoBruto double
|
||||
Volumes integer
|
||||
ItensPorCaixa integer
|
||||
Gtin string(50)
|
||||
GtinEmbalagem string(50)
|
||||
TipoProducao string(5)
|
||||
Condicao integer
|
||||
FreteGratis boolean
|
||||
Marca string(100)
|
||||
DescricaoComplementar string(4000)
|
||||
LinkExterno string(255)
|
||||
Observacoes string(255)
|
||||
Categoria_id integer
|
||||
Estoque_minimo integer
|
||||
Estoque_maximo integer
|
||||
Estoque_crossdocking integer
|
||||
Estoque_localizacao string(50)
|
||||
ActionEstoque string(50)
|
||||
Dimensoes_largura double
|
||||
Dimensoes_altura double
|
||||
Dimensoes_profundidade double
|
||||
Dimensoes_unidadeMedida double
|
||||
Tributacao_origem integer
|
||||
Tributacao_nFCI string(50)
|
||||
Tributacao_ncm string(50)
|
||||
Tributacao_cest string(50)
|
||||
Tributacao_codigoListaServicos string(50)
|
||||
Tributacao_spedTipoItem string(50)
|
||||
Tributacao_codigoItem string(50)
|
||||
Tributacao_percentualTributos double
|
||||
Tributacao_valorBaseStRetencao double
|
||||
Tributacao_valorStRetencao double
|
||||
Tributacao_valorICMSSubstituto double
|
||||
Tributacao_codigoExcecaoTipi string(50)
|
||||
Tributacao_classeEnquadramentoIpi string(50)
|
||||
Tributacao_valorIpiFixo double
|
||||
Tributacao_codigoSeloIpi string(50)
|
||||
Tributacao_valorPisFixo double
|
||||
Tributacao_valorCofinsFixo double
|
||||
Tributacao_dadosAdicionais string(50)
|
||||
GrupoProduto_id number
|
||||
Midia_video_url string(255)
|
||||
Midia_imagens_externas_0_link string(255)
|
||||
LinhaProduto_id number
|
||||
Estrutura_tipoEstoque string(5)
|
||||
Estrutura_lancamentoEstoque string(5)
|
||||
Estrutura_componentes_0_produto_id number
|
||||
Estrutura_componentes_0_produto_Quantidade double
|
||||
END TABLE
|
||||
|
||||
TABLE Depositos ON maria
|
||||
Internal_Id number key
|
||||
Id number
|
||||
Sku string(20)
|
||||
SaldoFisico double
|
||||
SaldoVirtual double
|
||||
END TABLE
|
||||
|
||||
TABLE Vendedores ON maria
|
||||
Id number key
|
||||
DescontoLimite double
|
||||
Loja_Id number
|
||||
Contato_Id number
|
||||
Contato_Nome string(100)
|
||||
Contato_Situacao string(1)
|
||||
END TABLE
|
||||
|
||||
TABLE ProdutoFornecedor ON maria
|
||||
Id number key
|
||||
Descricao string(255)
|
||||
Codigo string(50)
|
||||
PrecoCusto double
|
||||
PrecoCompra double
|
||||
Padrao boolean
|
||||
Produto_id number
|
||||
Fornecedor_id number
|
||||
Garantia integer
|
||||
END TABLE
|
||||
|
||||
TABLE ContasAPagar ON maria
|
||||
Id number key
|
||||
Situacao integer
|
||||
Vencimento date
|
||||
Valor double
|
||||
Contato_id number
|
||||
FormaPagamento_id number
|
||||
Saldo double
|
||||
DataEmissao date
|
||||
VencimentoOriginal date
|
||||
NumeroDocumento string(50)
|
||||
Competencia date
|
||||
Historico string(255)
|
||||
NumeroBanco string(10)
|
||||
Portador_id number
|
||||
Categoria_id number
|
||||
Borderos string(255)
|
||||
Ocorrencia_tipo integer
|
||||
END TABLE
|
||||
|
||||
TABLE ContasAReceber ON maria
|
||||
Id number key
|
||||
Situacao integer
|
||||
Vencimento date
|
||||
Valor double
|
||||
IdTransacao string(50)
|
||||
LinkQRCodePix string(255)
|
||||
LinkBoleto string(255)
|
||||
DataEmissao date
|
||||
Contato_id number
|
||||
Contato_nome string(150)
|
||||
Contato_numeroDocumento string(20)
|
||||
Contato_tipo string(1)
|
||||
FormaPagamento_id number
|
||||
FormaPagamento_codigoFiscal integer
|
||||
ContaContabil_id number
|
||||
ContaContabil_descricao string(255)
|
||||
Origem_id number
|
||||
Origem_tipoOrigem string(20)
|
||||
Origem_numero string(20)
|
||||
Origem_dataEmissao date
|
||||
Origem_valor double
|
||||
Origem_situacao integer
|
||||
Origem_url string(255)
|
||||
Saldo double
|
||||
VencimentoOriginal date
|
||||
NumeroDocumento string(50)
|
||||
Competencia date
|
||||
Historico string(255)
|
||||
NumeroBanco string(10)
|
||||
Portador_id number
|
||||
Categoria_id number
|
||||
Vendedor_id number
|
||||
Borderos string(255)
|
||||
Ocorrencia_tipo integer
|
||||
END TABLE
|
||||
|
||||
TABLE CategoriaReceita ON maria
|
||||
Id number key
|
||||
IdCategoriaPai number
|
||||
Descricao string(255)
|
||||
Tipo integer
|
||||
Situacao integer
|
||||
END TABLE
|
||||
|
||||
TABLE FormaDePagamento ON maria
|
||||
Id number key
|
||||
Descricao string(255)
|
||||
TipoPagamento integer
|
||||
Situacao integer
|
||||
Fixa boolean
|
||||
Padrao integer
|
||||
Finalidade integer
|
||||
Condicao string(10)
|
||||
Destino integer
|
||||
Taxas_aliquota double
|
||||
Taxas_valor double
|
||||
Taxas_prazo integer
|
||||
DadosCartao_bandeira integer
|
||||
DadosCartao_tipo integer
|
||||
DadosCartao_cnpjCredenciadora string(16)
|
||||
END TABLE
|
||||
|
||||
TABLE NaturezaDeOperacao ON maria
|
||||
Id number key
|
||||
Situacao integer
|
||||
Padrao integer
|
||||
Descricao string(255)
|
||||
END TABLE
|
||||
|
||||
TABLE Parcela ON maria
|
||||
Id number key
|
||||
Pedido_id number
|
||||
DataVencimento date
|
||||
Valor double
|
||||
Observacoes string(255)
|
||||
FormaPagamento_id number
|
||||
END TABLE
|
||||
|
||||
TABLE HistoricoPreco ON maria
|
||||
Id number key
|
||||
Sku string(50)
|
||||
PrecoAntigo double
|
||||
PrecoAtual double
|
||||
Produto_id number
|
||||
DataModificado date
|
||||
END TABLE
|
||||
133
bling.gbai/bling.gbkb/bling-erp-guide.md
Normal file
133
bling.gbai/bling.gbkb/bling-erp-guide.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# Bling ERP Integration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Bling is a Brazilian ERP (Enterprise Resource Planning) system designed for small and medium businesses. This bot integrates with Bling to provide inventory management, order processing, and data synchronization capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
### Inventory Management
|
||||
|
||||
The bot can help you with:
|
||||
|
||||
- **Stock Consultation**: Check current inventory levels for any product
|
||||
- **Stock Adjustments**: Add or remove items from inventory
|
||||
- **Low Stock Alerts**: Get notified when products fall below minimum levels
|
||||
- **Multi-warehouse Support**: Track inventory across multiple locations
|
||||
|
||||
### Order Processing
|
||||
|
||||
- **Create Orders**: Process new sales orders through conversation
|
||||
- **Order Status**: Check the status of existing orders
|
||||
- **Product Options**: Select colors, sizes, and variants when ordering
|
||||
- **Accompanying Items**: Add related products to orders (e.g., adding a chalk box with a chalkboard)
|
||||
|
||||
### Data Synchronization
|
||||
|
||||
- **Sync ERP**: Synchronize all data with Bling
|
||||
- **Sync Inventory**: Update inventory levels from Bling
|
||||
- **Sync Accounts**: Synchronize customer and supplier accounts
|
||||
- **Sync Suppliers**: Update supplier information
|
||||
|
||||
### Data Analysis
|
||||
|
||||
- **Sales Reports**: Generate sales reports and insights
|
||||
- **Inventory Analysis**: Analyze stock movement patterns
|
||||
- **Performance Metrics**: View key business indicators
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before using the Bling integration, ensure you have:
|
||||
|
||||
1. An active Bling account
|
||||
2. API credentials configured
|
||||
3. Products registered in Bling
|
||||
|
||||
### Common Commands
|
||||
|
||||
| Action | Example |
|
||||
|--------|---------|
|
||||
| Check stock | "Consultar estoque do produto X" |
|
||||
| Place order | "Fazer pedido" |
|
||||
| Sync data | "Sincronizar ERP" |
|
||||
| Data analysis | "Análise de dados" |
|
||||
|
||||
## Product Selection
|
||||
|
||||
When placing an order, the bot will:
|
||||
|
||||
1. Show available products from the JSON catalog
|
||||
2. Offer color and size options when applicable
|
||||
3. Allow selection of accompanying items
|
||||
4. Confirm the order with customer name and items
|
||||
|
||||
## Order Structure
|
||||
|
||||
Orders contain:
|
||||
|
||||
- **Customer Name**: Who is placing the order
|
||||
- **Order Items**: Main products selected (one item at a time)
|
||||
- **Accompanying Items**: Additional related products
|
||||
- **Product ID**: Matches the JSON product catalog for correlation
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I check if a product is in stock?**
|
||||
A: Ask "Consultar estoque" and provide the product name or code.
|
||||
|
||||
**Q: How do I synchronize data with Bling?**
|
||||
A: Say "Sincronizar ERP" or select the sync option from suggestions.
|
||||
|
||||
**Q: Can I place orders for multiple items?**
|
||||
A: Yes, but items are added one at a time. The bot will ask if you want to add more items.
|
||||
|
||||
**Q: How often should I sync inventory?**
|
||||
A: It's recommended to sync at least daily, or after significant sales activity.
|
||||
|
||||
**Q: What if a product shows different stock in Bling vs. the bot?**
|
||||
A: Run a full inventory sync to update the bot's data from Bling.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
If you experience connection problems:
|
||||
|
||||
1. Verify API credentials are correct
|
||||
2. Check that Bling services are online
|
||||
3. Retry the sync operation
|
||||
|
||||
### Stock Discrepancies
|
||||
|
||||
If stock levels don't match:
|
||||
|
||||
1. Run "Sincronizar estoque"
|
||||
2. Check for pending orders in Bling
|
||||
3. Verify no manual adjustments were made outside the system
|
||||
|
||||
### Order Failures
|
||||
|
||||
If an order fails to process:
|
||||
|
||||
1. Verify product availability
|
||||
2. Check customer information is complete
|
||||
3. Ensure Bling API is responding
|
||||
4. Review error logs for details
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Sync**: Sync data at the start of each business day
|
||||
2. **Stock Verification**: Verify stock before confirming large orders
|
||||
3. **Complete Information**: Always provide complete customer details
|
||||
4. **Order Confirmation**: Review orders before final submission
|
||||
5. **Data Backup**: Regularly export data for backup purposes
|
||||
|
||||
## Support
|
||||
|
||||
For technical issues with the Bling integration:
|
||||
|
||||
- Check Bling API documentation
|
||||
- Review General Bots logs for errors
|
||||
- Contact your system administrator
|
||||
25
bling.gbai/bling.gbot/config.csv
Normal file
25
bling.gbai/bling.gbot/config.csv
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
name,value
|
||||
,
|
||||
bot-name,Bling ERP Integration
|
||||
bot-description,Synchronizes data with Bling ERP system
|
||||
,
|
||||
param-blingClientID,
|
||||
param-blingClientSecret,
|
||||
param-blingHost,https://api.bling.com.br/Api/v3
|
||||
param-blingTenant,
|
||||
,
|
||||
param-host,https://api.bling.com.br/Api/v3
|
||||
param-limit,100
|
||||
param-pages,50
|
||||
,
|
||||
param-admin1,admin@yourdomain.com
|
||||
param-admin2,admin2@yourdomain.com
|
||||
,
|
||||
llm-model,gpt-4o-mini
|
||||
llm-temperature,0.3
|
||||
,
|
||||
sync-interval,21600
|
||||
sync-products,true
|
||||
sync-orders,true
|
||||
sync-contacts,true
|
||||
sync-inventory,true
|
||||
|
51
broadcast.gbai/broadcast.gbdialog/broadcast.bas
Normal file
51
broadcast.gbai/broadcast.gbdialog/broadcast.bas
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
PARAM message AS STRING LIKE "Hello {name}, how are you?" DESCRIPTION "Message to broadcast, supports {name} and {mobile} variables"
|
||||
PARAM listfile AS STRING LIKE "broadcast.csv" DESCRIPTION "CSV file with contacts (name, mobile columns)"
|
||||
PARAM filter AS STRING LIKE "status=active" DESCRIPTION "Filter condition for contact list" OPTIONAL
|
||||
|
||||
DESCRIPTION "Send broadcast message to a list of contacts from CSV file"
|
||||
|
||||
IF NOT listfile THEN
|
||||
listfile = "broadcast.csv"
|
||||
END IF
|
||||
|
||||
IF filter THEN
|
||||
list = FIND listfile, filter
|
||||
ELSE
|
||||
list = FIND listfile
|
||||
END IF
|
||||
|
||||
IF UBOUND(list) = 0 THEN
|
||||
TALK "No contacts found in " + listfile
|
||||
RETURN 0
|
||||
END IF
|
||||
|
||||
index = 1
|
||||
sent = 0
|
||||
|
||||
DO WHILE index < UBOUND(list)
|
||||
row = list[index]
|
||||
|
||||
msg = REPLACE(message, "{name}", row.name)
|
||||
msg = REPLACE(msg, "{mobile}", row.mobile)
|
||||
|
||||
TALK TO row.mobile, msg
|
||||
WAIT 5
|
||||
|
||||
WITH logEntry
|
||||
timestamp = NOW()
|
||||
user = USERNAME
|
||||
from = FROM
|
||||
mobile = row.mobile
|
||||
name = row.name
|
||||
status = "sent"
|
||||
END WITH
|
||||
|
||||
SAVE "Log.xlsx", logEntry
|
||||
|
||||
sent = sent + 1
|
||||
index = index + 1
|
||||
LOOP
|
||||
|
||||
TALK "Broadcast sent to " + sent + " contacts."
|
||||
|
||||
RETURN sent
|
||||
122
broadcast.gbai/broadcast.gbkb/broadcast-guide.md
Normal file
122
broadcast.gbai/broadcast.gbkb/broadcast-guide.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Broadcast Messaging Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Broadcast feature allows you to send messages to multiple contacts simultaneously using WhatsApp or other messaging channels. This is ideal for announcements, marketing campaigns, and bulk notifications.
|
||||
|
||||
## How to Send a Broadcast
|
||||
|
||||
### Basic Broadcast
|
||||
|
||||
To send a broadcast message, you need:
|
||||
1. A message template with optional personalization variables
|
||||
2. A CSV file containing your contact list
|
||||
|
||||
### Message Variables
|
||||
|
||||
You can personalize messages using these variables:
|
||||
- `{name}` - Replaced with the contact's name
|
||||
- `{mobile}` - Replaced with the contact's phone number
|
||||
|
||||
**Example:**
|
||||
```
|
||||
Hello {name}, we have exciting news to share with you!
|
||||
```
|
||||
|
||||
### Contact List Format
|
||||
|
||||
Your CSV file should have the following columns:
|
||||
- `name` - Contact's name
|
||||
- `mobile` - Phone number in international format (e.g., +5511999999999)
|
||||
- Additional columns can be used for filtering
|
||||
|
||||
**Example broadcast.csv:**
|
||||
```
|
||||
name,mobile,status
|
||||
John Smith,+5511999999999,active
|
||||
Maria Garcia,+5521888888888,active
|
||||
Carlos Santos,+5531777777777,inactive
|
||||
```
|
||||
|
||||
## Filtering Contacts
|
||||
|
||||
You can filter your contact list using conditions:
|
||||
- `status=active` - Only send to active contacts
|
||||
- `region=south` - Filter by custom fields
|
||||
- Multiple filters can be combined
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Message Content
|
||||
|
||||
1. **Keep it concise** - Short messages have higher engagement
|
||||
2. **Personalize** - Use `{name}` to make messages feel personal
|
||||
3. **Clear call-to-action** - Tell recipients what to do next
|
||||
4. **Timing** - Send during appropriate business hours
|
||||
|
||||
### Contact Management
|
||||
|
||||
1. **Clean your list** - Remove invalid numbers regularly
|
||||
2. **Respect opt-outs** - Remove contacts who don't want messages
|
||||
3. **Update regularly** - Keep contact information current
|
||||
4. **Segment audiences** - Use filters for targeted messaging
|
||||
|
||||
### Compliance
|
||||
|
||||
1. **Get consent** - Only message contacts who opted in
|
||||
2. **Identify yourself** - Make clear who is sending the message
|
||||
3. **Provide opt-out** - Include instructions to unsubscribe
|
||||
4. **Follow local laws** - LGPD, GDPR, TCPA requirements apply
|
||||
|
||||
## Rate Limits
|
||||
|
||||
To prevent spam detection and ensure delivery:
|
||||
- Messages are sent with a 5-second delay between each
|
||||
- WhatsApp Business API limits apply
|
||||
- Large broadcasts may take time to complete
|
||||
|
||||
## Logging and Tracking
|
||||
|
||||
All broadcast operations are logged to `Log.xlsx` with:
|
||||
- Timestamp
|
||||
- User who initiated the broadcast
|
||||
- Recipient mobile number
|
||||
- Recipient name
|
||||
- Delivery status
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Messages Not Sending
|
||||
|
||||
- Verify phone numbers are in international format
|
||||
- Check that the CSV file exists and has correct columns
|
||||
- Ensure the bot has messaging permissions
|
||||
|
||||
### Some Contacts Skipped
|
||||
|
||||
- Contact may have blocked the number
|
||||
- Phone number format may be incorrect
|
||||
- WhatsApp account may not exist for that number
|
||||
|
||||
### Slow Delivery
|
||||
|
||||
- Large lists take time due to rate limiting
|
||||
- This is intentional to prevent spam flags
|
||||
- Check Log.xlsx for progress
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How many contacts can I message at once?**
|
||||
A: There's no hard limit, but larger lists will take longer due to rate limiting.
|
||||
|
||||
**Q: Can I schedule broadcasts for later?**
|
||||
A: Yes, use scheduled jobs to set up future broadcasts.
|
||||
|
||||
**Q: Will I know if messages were delivered?**
|
||||
A: The log file tracks send status. Delivery confirmation depends on the messaging platform.
|
||||
|
||||
**Q: Can I send images or files?**
|
||||
A: The basic broadcast sends text. For media, use dedicated media broadcast tools.
|
||||
|
||||
**Q: How do I stop a broadcast in progress?**
|
||||
A: Contact an administrator to stop the process if needed.
|
||||
174
compliance/hipaa-medical.gbai/README.md
Normal file
174
compliance/hipaa-medical.gbai/README.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# HIPAA Medical Privacy Template
|
||||
|
||||
A HIPAA-compliant healthcare privacy portal template for General Bots.
|
||||
|
||||
## Overview
|
||||
|
||||
This template provides healthcare organizations with a ready-to-deploy patient privacy rights management system that complies with:
|
||||
|
||||
- **HIPAA** (Health Insurance Portability and Accountability Act)
|
||||
- **HITECH Act** (Health Information Technology for Economic and Clinical Health)
|
||||
- State-specific healthcare privacy regulations
|
||||
|
||||
## Features
|
||||
|
||||
### Patient Rights Management
|
||||
|
||||
1. **Access Medical Records** - Patients can request copies of their Protected Health Information (PHI)
|
||||
2. **Request Amendments** - Patients can request corrections to their medical records
|
||||
3. **Accounting of Disclosures** - Track and report who has accessed patient PHI
|
||||
4. **Request Restrictions** - Allow patients to limit how their PHI is used or shared
|
||||
5. **Confidential Communications** - Patients can specify preferred contact methods
|
||||
6. **File Privacy Complaints** - Streamlined complaint submission process
|
||||
7. **Revoke Authorizations** - Withdraw previous consent for PHI disclosure
|
||||
|
||||
### HIPAA Compliance Features
|
||||
|
||||
- **Audit Trail** - Complete logging of all PHI access and requests
|
||||
- **Encryption** - AES-256 at rest, TLS 1.3 in transit
|
||||
- **Access Controls** - Role-based access control (RBAC)
|
||||
- **Break Glass** - Emergency access procedures with audit
|
||||
- **Minimum Necessary** - Automatic enforcement of minimum necessary standard
|
||||
- **PHI Detection** - Automatic detection and redaction of PHI in communications
|
||||
- **Breach Notification** - Built-in breach response workflow
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy this template to your General Bots instance:
|
||||
|
||||
```bash
|
||||
cp -r templates/hipaa-medical.gbai /path/to/your/bot/
|
||||
```
|
||||
|
||||
2. Configure the bot settings in `hipaa.gbot/config.csv`:
|
||||
|
||||
```csv
|
||||
Covered Entity Name,Your Healthcare Organization
|
||||
Privacy Officer Email,privacy@yourhealthcare.org
|
||||
HIPAA Security Officer,security@yourhealthcare.org
|
||||
```
|
||||
|
||||
3. Deploy the template:
|
||||
|
||||
```bash
|
||||
botserver --deploy hipaa-medical.gbai
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Required Settings
|
||||
|
||||
| Setting | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `Covered Entity Name` | Your organization's legal name | Memorial Hospital |
|
||||
| `Privacy Officer Email` | HIPAA Privacy Officer contact | privacy@hospital.org |
|
||||
| `HIPAA Security Officer` | Security Officer contact | security@hospital.org |
|
||||
| `Covered Entity NPI` | National Provider Identifier | 1234567890 |
|
||||
|
||||
### Security Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `Require 2FA` | true | Two-factor authentication required |
|
||||
| `Session Timeout` | 300 | Session timeout in seconds (5 minutes) |
|
||||
| `Encryption At Rest` | AES-256 | Data encryption standard |
|
||||
| `PHI Auto Redaction` | true | Automatically redact PHI in logs |
|
||||
|
||||
### Compliance Timelines
|
||||
|
||||
| Requirement | Deadline | Setting |
|
||||
|-------------|----------|---------|
|
||||
| Access Requests | 30 days | `Response SLA Hours` |
|
||||
| Urgent Requests | 48 hours | `Urgent Response Hours` |
|
||||
| Breach Notification | 60 hours | `Breach Notification Hours` |
|
||||
|
||||
## Dialogs
|
||||
|
||||
### Main Entry Point
|
||||
|
||||
- `start.bas` - Main menu for patient privacy rights
|
||||
|
||||
### Patient Rights Dialogs
|
||||
|
||||
- `access-phi.bas` - Request medical records
|
||||
- `request-amendment.bas` - Request record corrections
|
||||
- `accounting-disclosures.bas` - View access history
|
||||
- `request-restrictions.bas` - Limit PHI use/sharing
|
||||
- `confidential-communications.bas` - Set contact preferences
|
||||
- `file-complaint.bas` - Submit privacy complaints
|
||||
- `revoke-authorization.bas` - Withdraw consent
|
||||
|
||||
## Integration
|
||||
|
||||
### Patient Portal Integration
|
||||
|
||||
Connect to your existing patient portal:
|
||||
|
||||
```basic
|
||||
' In your custom dialog
|
||||
patient = GET PATIENT FROM "portal" WHERE mrn = patient_mrn
|
||||
IF patient.verified THEN
|
||||
CALL "access-phi.bas"
|
||||
END IF
|
||||
```
|
||||
|
||||
### EHR Integration
|
||||
|
||||
The template can integrate with common EHR systems:
|
||||
|
||||
- Epic
|
||||
- Cerner
|
||||
- Meditech
|
||||
- Allscripts
|
||||
|
||||
Configure your EHR connection in the bot settings or use the FHIR API for standard integration.
|
||||
|
||||
## Audit Requirements
|
||||
|
||||
All interactions are logged to the `hipaa_audit_log` table with:
|
||||
|
||||
- Timestamp
|
||||
- Session ID
|
||||
- Action performed
|
||||
- User/patient identifier
|
||||
- IP address
|
||||
- User agent
|
||||
- Outcome
|
||||
|
||||
Retain audit logs for a minimum of 6 years (2,190 days) per HIPAA requirements.
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding Custom Dialogs
|
||||
|
||||
Create new `.bas` files in the `hipaa.gbdialog` folder:
|
||||
|
||||
```basic
|
||||
' custom-workflow.bas
|
||||
TALK "Starting custom HIPAA workflow..."
|
||||
' Your custom logic here
|
||||
```
|
||||
|
||||
### Branding
|
||||
|
||||
Customize the welcome message and organization details in `config.csv`.
|
||||
|
||||
## Support
|
||||
|
||||
For questions about this template:
|
||||
|
||||
- **Documentation**: See General Bots docs
|
||||
- **Issues**: GitHub Issues
|
||||
- **HIPAA Guidance**: consult your compliance officer
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This template is provided as a compliance aid and does not constitute legal advice. Healthcare organizations are responsible for ensuring their HIPAA compliance program meets all regulatory requirements. Consult with healthcare compliance professionals and legal counsel.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - See LICENSE file in the main repository.
|
||||
|
||||
---
|
||||
|
||||
Built with ❤️ by Pragmatismo
|
||||
88
compliance/hipaa-medical.gbai/hipaa.gbdialog/start.bas
Normal file
88
compliance/hipaa-medical.gbai/hipaa.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
' =============================================================================
|
||||
' HIPAA Medical Privacy Portal - Main Dialog
|
||||
' General Bots Template for Healthcare Data Protection
|
||||
' =============================================================================
|
||||
' This template helps healthcare organizations comply with:
|
||||
' - HIPAA (Health Insurance Portability and Accountability Act)
|
||||
' - HITECH Act (Health Information Technology for Economic and Clinical Health)
|
||||
' - State-specific healthcare privacy regulations
|
||||
' =============================================================================
|
||||
|
||||
TALK "🏥 Welcome to the HIPAA Privacy Portal"
|
||||
TALK "I can help you manage your Protected Health Information (PHI) rights."
|
||||
TALK ""
|
||||
|
||||
TALK "Under HIPAA, you have the following rights:"
|
||||
TALK ""
|
||||
TALK "1️⃣ **Access Your Medical Records** - Request copies of your health information"
|
||||
TALK "2️⃣ **Request Amendments** - Correct errors in your medical records"
|
||||
TALK "3️⃣ **Accounting of Disclosures** - See who has accessed your PHI"
|
||||
TALK "4️⃣ **Request Restrictions** - Limit how we use or share your information"
|
||||
TALK "5️⃣ **Confidential Communications** - Choose how we contact you"
|
||||
TALK "6️⃣ **File a Privacy Complaint** - Report a privacy concern"
|
||||
TALK "7️⃣ **Revoke Authorization** - Withdraw previous consent for PHI disclosure"
|
||||
|
||||
HEAR choice AS "What would you like to do? (1-7 or describe your request)"
|
||||
|
||||
SELECT CASE choice
|
||||
CASE "1", "access", "records", "medical records", "view", "copy"
|
||||
CALL "access-phi.bas"
|
||||
|
||||
CASE "2", "amend", "amendment", "correct", "correction", "fix", "error"
|
||||
CALL "request-amendment.bas"
|
||||
|
||||
CASE "3", "accounting", "disclosures", "who accessed", "access log"
|
||||
CALL "accounting-disclosures.bas"
|
||||
|
||||
CASE "4", "restrict", "restriction", "limit", "limitations"
|
||||
CALL "request-restrictions.bas"
|
||||
|
||||
CASE "5", "communications", "contact", "how to contact", "confidential"
|
||||
CALL "confidential-communications.bas"
|
||||
|
||||
CASE "6", "complaint", "report", "privacy concern", "violation"
|
||||
CALL "file-complaint.bas"
|
||||
|
||||
CASE "7", "revoke", "withdraw", "cancel authorization"
|
||||
CALL "revoke-authorization.bas"
|
||||
|
||||
CASE ELSE
|
||||
' Use LLM to understand medical privacy requests
|
||||
SET CONTEXT "You are a HIPAA compliance assistant. Classify the user's request into one of these categories: access_records, amendment, disclosures, restrictions, communications, complaint, revoke. Only respond with the category name."
|
||||
|
||||
intent = LLM "Classify this healthcare privacy request: " + choice
|
||||
|
||||
SELECT CASE intent
|
||||
CASE "access_records"
|
||||
CALL "access-phi.bas"
|
||||
CASE "amendment"
|
||||
CALL "request-amendment.bas"
|
||||
CASE "disclosures"
|
||||
CALL "accounting-disclosures.bas"
|
||||
CASE "restrictions"
|
||||
CALL "request-restrictions.bas"
|
||||
CASE "communications"
|
||||
CALL "confidential-communications.bas"
|
||||
CASE "complaint"
|
||||
CALL "file-complaint.bas"
|
||||
CASE "revoke"
|
||||
CALL "revoke-authorization.bas"
|
||||
CASE ELSE
|
||||
TALK "I'm not sure I understood your request."
|
||||
TALK "Please select a number from 1-7 or contact our Privacy Officer directly."
|
||||
TALK ""
|
||||
TALK "📞 Privacy Officer: privacy@healthcare.org"
|
||||
TALK "📧 Email: hipaa-requests@healthcare.org"
|
||||
CALL "start.bas"
|
||||
END SELECT
|
||||
END SELECT
|
||||
|
||||
' Log all interactions for HIPAA audit trail
|
||||
INSERT INTO "hipaa_audit_log" VALUES {
|
||||
"timestamp": NOW(),
|
||||
"session_id": GET SESSION "id",
|
||||
"action": "privacy_portal_access",
|
||||
"choice": choice,
|
||||
"ip_address": GET SESSION "client_ip",
|
||||
"user_agent": GET SESSION "user_agent"
|
||||
}
|
||||
63
compliance/hipaa-medical.gbai/hipaa.gbot/config.csv
Normal file
63
compliance/hipaa-medical.gbai/hipaa.gbot/config.csv
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
name,value
|
||||
Bot Name,HIPAA Medical Privacy Center
|
||||
Bot Description,Healthcare Data Protection and Patient Rights Management Bot
|
||||
Bot Version,1.0.0
|
||||
Bot Author,Pragmatismo
|
||||
Bot License,AGPL-3.0
|
||||
Bot Category,Healthcare Compliance
|
||||
Bot Tags,hipaa;healthcare;phi;medical;compliance;privacy;patient-rights
|
||||
Default Language,en
|
||||
Supported Languages,en;es;pt
|
||||
Welcome Message,Welcome to the Healthcare Privacy Center. I can help you with your patient rights under HIPAA, including accessing your medical records, requesting amendments, and managing your health information privacy.
|
||||
Error Message,I apologize, but I encountered an issue processing your request. For urgent matters, please contact our Privacy Officer directly at privacy@healthcare.org
|
||||
Timeout Message,Your session has timed out for security. Please start a new conversation. This is required to protect your health information.
|
||||
Session Timeout,300
|
||||
Max Retries,3
|
||||
Log Level,info
|
||||
Enable Audit Log,true
|
||||
Audit Log Retention Days,2190
|
||||
Require Authentication,true
|
||||
Require Email Verification,true
|
||||
Require 2FA,true
|
||||
Data Retention Days,2190
|
||||
Auto Delete Completed Requests,false
|
||||
Send Confirmation Emails,true
|
||||
Privacy Officer Email,privacy@healthcare.org
|
||||
HIPAA Privacy Officer,hipaa-officer@healthcare.org
|
||||
HIPAA Security Officer,security-officer@healthcare.org
|
||||
Covered Entity Name,Your Healthcare Organization
|
||||
Covered Entity Address,123 Medical Center Drive
|
||||
Covered Entity NPI,1234567890
|
||||
Compliance Frameworks,HIPAA;HITECH;State-Privacy-Laws
|
||||
Response SLA Hours,30
|
||||
Urgent Response Hours,48
|
||||
Escalation Email,compliance@healthcare.org
|
||||
Enable HIPAA Mode,true
|
||||
PHI Processing Enabled,true
|
||||
PHI Detection Enabled,true
|
||||
PHI Auto Redaction,true
|
||||
Minimum Necessary Standard,true
|
||||
Encryption At Rest,AES-256
|
||||
Encryption In Transit,TLS-1.3
|
||||
Access Control Model,RBAC
|
||||
Break Glass Enabled,true
|
||||
Break Glass Audit Required,true
|
||||
Consent Required,true
|
||||
Authorization Form Version,2.0
|
||||
NPP Version,3.0
|
||||
NPP Last Updated,2025-01-01
|
||||
Designated Record Set,true
|
||||
Accounting of Disclosures,true
|
||||
Restriction Requests Enabled,true
|
||||
Confidential Communications,true
|
||||
Patient Portal URL,https://portal.healthcare.org
|
||||
HIE Participation,true
|
||||
E-Prescribing Enabled,true
|
||||
Telehealth Enabled,true
|
||||
BAA Required,true
|
||||
Workforce Training Required,true
|
||||
Training Frequency Days,365
|
||||
Incident Response Plan,true
|
||||
Breach Notification Hours,60
|
||||
OCR Complaint URL,https://www.hhs.gov/hipaa/filing-a-complaint
|
||||
State AG Contact,state-ag@state.gov
|
||||
|
Can't render this file because it has a wrong number of fields in line 11.
|
200
compliance/privacy.gbai/README.md
Normal file
200
compliance/privacy.gbai/README.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Privacy Rights Center Template (privacy.gbai)
|
||||
|
||||
A comprehensive LGPD/GDPR compliance template for General Bots that enables organizations to handle data subject rights requests automatically.
|
||||
|
||||
## Overview
|
||||
|
||||
This template provides a complete privacy portal that helps organizations comply with:
|
||||
|
||||
- **LGPD** (Lei Geral de Proteção de Dados - Brazil)
|
||||
- **GDPR** (General Data Protection Regulation - EU)
|
||||
- **CCPA** (California Consumer Privacy Act - US)
|
||||
- **Other privacy regulations** with similar data subject rights
|
||||
|
||||
## Features
|
||||
|
||||
### Data Subject Rights Implemented
|
||||
|
||||
| Right | LGPD Article | GDPR Article | Dialog File |
|
||||
|-------|--------------|--------------|-------------|
|
||||
| Access | Art. 18 | Art. 15 | `request-data.bas` |
|
||||
| Rectification | Art. 18 III | Art. 16 | `rectify-data.bas` |
|
||||
| Erasure (Deletion) | Art. 18 VI | Art. 17 | `delete-data.bas` |
|
||||
| Data Portability | Art. 18 V | Art. 20 | `export-data.bas` |
|
||||
| Consent Management | Art. 8 | Art. 7 | `manage-consents.bas` |
|
||||
| Object to Processing | Art. 18 IV | Art. 21 | `object-processing.bas` |
|
||||
|
||||
### Key Capabilities
|
||||
|
||||
- **Identity Verification** - Email-based verification codes before processing requests
|
||||
- **Audit Trail** - Complete logging of all privacy requests for compliance
|
||||
- **Multi-format Export** - JSON, CSV, XML export options for data portability
|
||||
- **Consent Tracking** - Granular consent management with history
|
||||
- **Email Notifications** - Automated confirmations and reports
|
||||
- **SLA Tracking** - Response time monitoring (default: 72 hours)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy the template to your bot's packages directory:
|
||||
|
||||
```bash
|
||||
cp -r templates/privacy.gbai /path/to/your/bot/packages/
|
||||
```
|
||||
|
||||
2. Configure the bot settings in `privacy.gbot/config.csv`:
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Company Name,Your Company Name
|
||||
Privacy Officer Email,privacy@yourcompany.com
|
||||
DPO Contact,dpo@yourcompany.com
|
||||
```
|
||||
|
||||
3. Restart General Bots to load the template.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Required Settings
|
||||
|
||||
| Setting | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `Company Name` | Your organization name | Acme Corp |
|
||||
| `Privacy Officer Email` | Contact for privacy matters | privacy@acme.com |
|
||||
| `DPO Contact` | Data Protection Officer | dpo@acme.com |
|
||||
|
||||
### Optional Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `Session Timeout` | 900 | Session timeout in seconds |
|
||||
| `Response SLA Hours` | 72 | Max hours to respond to requests |
|
||||
| `Data Retention Days` | 30 | Days to retain completed request data |
|
||||
| `Enable HIPAA Mode` | false | Enable PHI handling features |
|
||||
| `Require 2FA` | false | Require two-factor authentication |
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
privacy.gbai/
|
||||
├── README.md # This file
|
||||
├── privacy.gbdialog/
|
||||
│ ├── start.bas # Main entry point
|
||||
│ ├── request-data.bas # Data access requests
|
||||
│ ├── delete-data.bas # Data erasure requests
|
||||
│ ├── export-data.bas # Data portability
|
||||
│ └── manage-consents.bas # Consent management
|
||||
├── privacy.gbot/
|
||||
│ └── config.csv # Bot configuration
|
||||
└── privacy.gbui/
|
||||
└── index.html # Web portal UI
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Starting the Privacy Portal
|
||||
|
||||
Users can access the privacy portal by saying:
|
||||
|
||||
- "I want to access my data"
|
||||
- "Delete my information"
|
||||
- "Export my data"
|
||||
- "Manage my consents"
|
||||
- Or selecting options 1-6 from the menu
|
||||
|
||||
### API Integration
|
||||
|
||||
The template exposes REST endpoints for integration:
|
||||
|
||||
```
|
||||
POST /api/privacy/request - Submit a new request
|
||||
GET /api/privacy/requests - List user's requests
|
||||
GET /api/privacy/request/:id - Get request status
|
||||
POST /api/privacy/consent - Update consents
|
||||
```
|
||||
|
||||
### Webhook Events
|
||||
|
||||
The template emits webhook events for integration:
|
||||
|
||||
- `privacy.request.created` - New request submitted
|
||||
- `privacy.request.completed` - Request fulfilled
|
||||
- `privacy.consent.updated` - Consent preferences changed
|
||||
- `privacy.data.deleted` - User data erased
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding Custom Consent Categories
|
||||
|
||||
Edit `manage-consents.bas` to add new consent categories:
|
||||
|
||||
```basic
|
||||
consent_categories = [
|
||||
{
|
||||
"id": "custom_category",
|
||||
"name": "Custom Category Name",
|
||||
"description": "Description for users",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Consent"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Branding the UI
|
||||
|
||||
Modify `privacy.gbui/index.html` to match your branding:
|
||||
|
||||
- Update CSS variables for colors
|
||||
- Replace logo and company name
|
||||
- Add custom legal text
|
||||
|
||||
### Email Templates
|
||||
|
||||
Customize email notifications by editing the `SEND MAIL` blocks in each dialog file.
|
||||
|
||||
## Compliance Notes
|
||||
|
||||
### Response Deadlines
|
||||
|
||||
| Regulation | Standard Deadline | Extended Deadline |
|
||||
|------------|-------------------|-------------------|
|
||||
| LGPD | 15 days | - |
|
||||
| GDPR | 30 days | 90 days (complex) |
|
||||
| CCPA | 45 days | 90 days |
|
||||
|
||||
### Data Retention
|
||||
|
||||
Some data may need to be retained for legal compliance:
|
||||
|
||||
- Financial records (tax requirements)
|
||||
- Legal dispute documentation
|
||||
- Fraud prevention records
|
||||
- Regulatory compliance data
|
||||
|
||||
The template handles this by anonymizing retained records while deleting identifiable information.
|
||||
|
||||
### Audit Requirements
|
||||
|
||||
All requests are logged to `privacy_requests` and `consent_history` tables with:
|
||||
|
||||
- Timestamp
|
||||
- User identifier
|
||||
- Request type
|
||||
- IP address
|
||||
- Completion status
|
||||
- Legal basis
|
||||
|
||||
## Support
|
||||
|
||||
For questions about this template:
|
||||
|
||||
- **Documentation**: https://docs.pragmatismo.com.br/privacy-template
|
||||
- **Issues**: https://github.com/GeneralBots/botserver/issues
|
||||
- **Email**: support@pragmatismo.com.br
|
||||
|
||||
## License
|
||||
|
||||
This template is part of General Bots and is licensed under AGPL-3.0.
|
||||
|
||||
---
|
||||
|
||||
**Note**: This template provides technical implementation for privacy compliance. Organizations should consult with legal counsel to ensure full compliance with applicable regulations.
|
||||
213
compliance/privacy.gbai/privacy.gbdialog/delete-data.bas
Normal file
213
compliance/privacy.gbai/privacy.gbdialog/delete-data.bas
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
' Privacy Template - Data Deletion Request (Right to be Forgotten)
|
||||
' LGPD Art. 18, GDPR Art. 17, HIPAA (where applicable)
|
||||
' This dialog handles user requests to delete their personal data
|
||||
|
||||
TALK "Data Deletion Request"
|
||||
TALK "I can help you exercise your right to have your personal data deleted."
|
||||
TALK "This is also known as the 'Right to be Forgotten' under LGPD and GDPR."
|
||||
|
||||
' Authenticate the user first
|
||||
TALK "For security purposes, I need to verify your identity before proceeding."
|
||||
HEAR email AS EMAIL WITH "Please enter your registered email address:"
|
||||
|
||||
' Verify email exists in system
|
||||
user = FIND "users.csv" WHERE email = email
|
||||
IF user IS NULL THEN
|
||||
TALK "I couldn't find an account with that email address."
|
||||
TALK "Please check the email and try again, or contact support@company.com"
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Send verification code
|
||||
verification_code = RANDOM(100000, 999999)
|
||||
SET BOT MEMORY "verification_" + email, verification_code
|
||||
SET BOT MEMORY "verification_expiry_" + email, NOW() + 15 * 60
|
||||
|
||||
SEND MAIL email, "Data Deletion Verification Code", "
|
||||
Your verification code is: " + verification_code + "
|
||||
|
||||
This code expires in 15 minutes.
|
||||
|
||||
If you did not request data deletion, please ignore this email and contact support immediately.
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
"
|
||||
|
||||
HEAR entered_code AS INTEGER WITH "I've sent a verification code to your email. Please enter it here:"
|
||||
|
||||
stored_code = GET BOT MEMORY "verification_" + email
|
||||
IF entered_code <> stored_code THEN
|
||||
TALK "Invalid verification code. Please try again."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
TALK "Identity verified."
|
||||
TALK ""
|
||||
TALK "What data would you like to delete?"
|
||||
TALK ""
|
||||
TALK "1. All my personal data (complete account deletion)"
|
||||
TALK "2. Conversation history only"
|
||||
TALK "3. Files and documents only"
|
||||
TALK "4. Activity logs and analytics"
|
||||
TALK "5. Specific data categories (I'll choose)"
|
||||
TALK "6. Cancel this request"
|
||||
|
||||
HEAR deletion_choice AS INTEGER WITH "Please enter your choice (1-6):"
|
||||
|
||||
SELECT CASE deletion_choice
|
||||
CASE 1
|
||||
deletion_type = "complete"
|
||||
TALK "Complete Account Deletion"
|
||||
TALK "This will permanently delete:"
|
||||
TALK "- Your user profile and account"
|
||||
TALK "- All conversation history"
|
||||
TALK "- All uploaded files and documents"
|
||||
TALK "- All activity logs"
|
||||
TALK "- All preferences and settings"
|
||||
TALK ""
|
||||
TALK "This action cannot be undone."
|
||||
|
||||
CASE 2
|
||||
deletion_type = "conversations"
|
||||
TALK "This will delete all your conversation history with our bots."
|
||||
|
||||
CASE 3
|
||||
deletion_type = "files"
|
||||
TALK "This will delete all files and documents you've uploaded."
|
||||
|
||||
CASE 4
|
||||
deletion_type = "logs"
|
||||
TALK "This will delete all activity logs and analytics data associated with you."
|
||||
|
||||
CASE 5
|
||||
deletion_type = "selective"
|
||||
TALK "Please specify which data categories you want deleted:"
|
||||
HEAR categories WITH "Enter categories separated by commas (e.g., 'email history, phone number, address'):"
|
||||
|
||||
CASE 6
|
||||
TALK "Request cancelled. No data has been deleted."
|
||||
EXIT
|
||||
|
||||
CASE ELSE
|
||||
TALK "Invalid choice. Please start over."
|
||||
EXIT
|
||||
END SELECT
|
||||
|
||||
' Explain data retention exceptions
|
||||
TALK ""
|
||||
TALK "Legal Notice:"
|
||||
TALK "Some data may be retained for legal compliance purposes:"
|
||||
TALK "- Financial records (tax requirements)"
|
||||
TALK "- Legal dispute documentation"
|
||||
TALK "- Fraud prevention records"
|
||||
TALK "- Regulatory compliance data"
|
||||
TALK ""
|
||||
TALK "Retained data will be minimized and protected according to law."
|
||||
|
||||
HEAR reason WITH "Please briefly explain why you're requesting deletion (optional, press Enter to skip):"
|
||||
|
||||
HEAR confirmation WITH "Type 'DELETE MY DATA' to confirm this irreversible action:"
|
||||
|
||||
IF confirmation <> "DELETE MY DATA" THEN
|
||||
TALK "Confirmation not received. Request cancelled for your protection."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Log the deletion request
|
||||
request_id = "DEL-" + FORMAT(NOW(), "YYYYMMDD") + "-" + RANDOM(10000, 99999)
|
||||
request_date = NOW()
|
||||
|
||||
' Create deletion request record
|
||||
INSERT INTO "deletion_requests.csv", request_id, email, deletion_type, categories, reason, request_date, "pending"
|
||||
|
||||
' Process the deletion based on type
|
||||
SELECT CASE deletion_type
|
||||
CASE "complete"
|
||||
' Delete from all tables
|
||||
DELETE FROM "messages" WHERE user_email = email
|
||||
DELETE FROM "files" WHERE owner_email = email
|
||||
DELETE FROM "activity_logs" WHERE user_email = email
|
||||
DELETE FROM "user_preferences" WHERE email = email
|
||||
DELETE FROM "sessions" WHERE user_email = email
|
||||
|
||||
' Anonymize required retention records
|
||||
UPDATE "audit_logs" SET user_email = "DELETED_USER_" + request_id WHERE user_email = email
|
||||
|
||||
' Mark user for deletion (actual deletion after retention period)
|
||||
UPDATE "users" SET status = "pending_deletion", deletion_request_id = request_id WHERE email = email
|
||||
|
||||
CASE "conversations"
|
||||
DELETE FROM "messages" WHERE user_email = email
|
||||
DELETE FROM "sessions" WHERE user_email = email
|
||||
|
||||
CASE "files"
|
||||
' Get file list for physical deletion
|
||||
files = FIND "files" WHERE owner_email = email
|
||||
FOR EACH file IN files
|
||||
DELETE FILE file.path
|
||||
NEXT
|
||||
DELETE FROM "files" WHERE owner_email = email
|
||||
|
||||
CASE "logs"
|
||||
DELETE FROM "activity_logs" WHERE user_email = email
|
||||
' Anonymize audit logs (keep for compliance but remove PII)
|
||||
UPDATE "audit_logs" SET user_email = "ANONYMIZED" WHERE user_email = email
|
||||
|
||||
CASE "selective"
|
||||
' Process specific categories
|
||||
TALK "Processing selective deletion for: " + categories
|
||||
' Custom handling based on categories specified
|
||||
INSERT INTO "manual_deletion_queue", request_id, email, categories, request_date
|
||||
END SELECT
|
||||
|
||||
' Update request status
|
||||
UPDATE "deletion_requests" SET status = "completed", completion_date = NOW() WHERE request_id = request_id
|
||||
|
||||
' Send confirmation email
|
||||
SEND MAIL email, "Data Deletion Request Confirmed - " + request_id, "
|
||||
Dear User,
|
||||
|
||||
Your data deletion request has been received and processed.
|
||||
|
||||
Request Details:
|
||||
- Request ID: " + request_id + "
|
||||
- Request Date: " + FORMAT(request_date, "YYYY-MM-DD HH:mm") + "
|
||||
- Deletion Type: " + deletion_type + "
|
||||
- Status: Completed
|
||||
|
||||
What happens next:
|
||||
" + IF(deletion_type = "complete", "
|
||||
- Your account will be fully deleted within 30 days
|
||||
- You will receive a final confirmation email
|
||||
- Some data may be retained for legal compliance (anonymized)
|
||||
", "
|
||||
- The specified data has been deleted from our systems
|
||||
- Some backups may take up to 30 days to purge
|
||||
") + "
|
||||
|
||||
Your Rights:
|
||||
- You can request a copy of any retained data
|
||||
- You can file a complaint with your data protection authority
|
||||
- Contact us at privacy@company.com for questions
|
||||
|
||||
Under LGPD (Brazil) and GDPR (EU), you have the right to:
|
||||
- Request confirmation of this deletion
|
||||
- Lodge a complaint with supervisory authorities
|
||||
- Seek judicial remedy if unsatisfied
|
||||
|
||||
Thank you for trusting us with your data.
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
Request Reference: " + request_id + "
|
||||
"
|
||||
|
||||
TALK ""
|
||||
TALK "Request Completed"
|
||||
TALK ""
|
||||
TALK "Your deletion request has been processed."
|
||||
TALK "Request ID: " + request_id
|
||||
TALK ""
|
||||
TALK "A confirmation email has been sent to " + email
|
||||
TALK ""
|
||||
TALK "If you have questions, contact privacy@company.com"
|
||||
TALK "Reference your Request ID in any communications."
|
||||
372
compliance/privacy.gbai/privacy.gbdialog/export-data.bas
Normal file
372
compliance/privacy.gbai/privacy.gbdialog/export-data.bas
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
' ============================================================================
|
||||
' Privacy Template: Data Portability/Export Request
|
||||
' LGPD Art. 18 V / GDPR Art. 20 - Right to Data Portability
|
||||
' ============================================================================
|
||||
' This dialog enables users to export their data in portable formats
|
||||
' Supports JSON, CSV, and XML export for interoperability
|
||||
|
||||
TALK "📦 **Data Portability Request**"
|
||||
TALK "You have the right to receive your personal data in a structured, commonly used, and machine-readable format."
|
||||
TALK ""
|
||||
|
||||
' Verify user identity
|
||||
TALK "First, I need to verify your identity."
|
||||
HEAR email AS EMAIL WITH "Please enter your registered email address:"
|
||||
|
||||
user = FIND "users" WHERE email = email
|
||||
IF user IS NULL THEN
|
||||
TALK "❌ No account found with that email address."
|
||||
TALK "Please check and try again, or contact support."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Send verification code
|
||||
code = GENERATE CODE 6
|
||||
SET SESSION "export_verification_code", code
|
||||
SET SESSION "export_email", email
|
||||
|
||||
SEND MAIL email, "Data Export Request - Verification Code", "
|
||||
Your verification code is: " + code + "
|
||||
|
||||
This code expires in 15 minutes.
|
||||
|
||||
If you did not request this data export, please ignore this email.
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
"
|
||||
|
||||
HEAR entered_code AS TEXT WITH "📧 Enter the verification code sent to your email:"
|
||||
|
||||
IF entered_code <> code THEN
|
||||
TALK "❌ Invalid verification code. Please start over."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
TALK "✅ Identity verified!"
|
||||
TALK ""
|
||||
|
||||
' Ask for export format
|
||||
TALK "**Choose your export format:**"
|
||||
TALK ""
|
||||
TALK "1️⃣ **JSON** - Best for importing into other systems"
|
||||
TALK "2️⃣ **CSV** - Best for spreadsheets (Excel, Google Sheets)"
|
||||
TALK "3️⃣ **XML** - Universal interchange format"
|
||||
TALK "4️⃣ **All formats** - Get all three formats in a ZIP file"
|
||||
|
||||
HEAR format_choice WITH "Enter your choice (1-4):"
|
||||
|
||||
SELECT CASE format_choice
|
||||
CASE "1", "json", "JSON"
|
||||
export_format = "json"
|
||||
format_name = "JSON"
|
||||
CASE "2", "csv", "CSV"
|
||||
export_format = "csv"
|
||||
format_name = "CSV"
|
||||
CASE "3", "xml", "XML"
|
||||
export_format = "xml"
|
||||
format_name = "XML"
|
||||
CASE "4", "all", "ALL"
|
||||
export_format = "all"
|
||||
format_name = "All Formats (ZIP)"
|
||||
CASE ELSE
|
||||
export_format = "json"
|
||||
format_name = "JSON"
|
||||
TALK "Defaulting to JSON format."
|
||||
END SELECT
|
||||
|
||||
TALK ""
|
||||
TALK "**Select data categories to export:**"
|
||||
TALK ""
|
||||
TALK "1️⃣ Everything (complete data export)"
|
||||
TALK "2️⃣ Profile information only"
|
||||
TALK "3️⃣ Conversations and messages"
|
||||
TALK "4️⃣ Files and documents"
|
||||
TALK "5️⃣ Activity history"
|
||||
TALK "6️⃣ Custom selection"
|
||||
|
||||
HEAR data_choice WITH "Enter your choice (1-6):"
|
||||
|
||||
' Define what data to export based on choice
|
||||
SELECT CASE data_choice
|
||||
CASE "1"
|
||||
include_profile = TRUE
|
||||
include_conversations = TRUE
|
||||
include_files = TRUE
|
||||
include_activity = TRUE
|
||||
include_consents = TRUE
|
||||
data_scope = "complete"
|
||||
|
||||
CASE "2"
|
||||
include_profile = TRUE
|
||||
include_conversations = FALSE
|
||||
include_files = FALSE
|
||||
include_activity = FALSE
|
||||
include_consents = TRUE
|
||||
data_scope = "profile"
|
||||
|
||||
CASE "3"
|
||||
include_profile = FALSE
|
||||
include_conversations = TRUE
|
||||
include_files = FALSE
|
||||
include_activity = FALSE
|
||||
include_consents = FALSE
|
||||
data_scope = "conversations"
|
||||
|
||||
CASE "4"
|
||||
include_profile = FALSE
|
||||
include_conversations = FALSE
|
||||
include_files = TRUE
|
||||
include_activity = FALSE
|
||||
include_consents = FALSE
|
||||
data_scope = "files"
|
||||
|
||||
CASE "5"
|
||||
include_profile = FALSE
|
||||
include_conversations = FALSE
|
||||
include_files = FALSE
|
||||
include_activity = TRUE
|
||||
include_consents = FALSE
|
||||
data_scope = "activity"
|
||||
|
||||
CASE "6"
|
||||
TALK "Select categories (yes/no for each):"
|
||||
HEAR include_profile AS BOOLEAN WITH "Include profile information?"
|
||||
HEAR include_conversations AS BOOLEAN WITH "Include conversations?"
|
||||
HEAR include_files AS BOOLEAN WITH "Include files metadata?"
|
||||
HEAR include_activity AS BOOLEAN WITH "Include activity logs?"
|
||||
HEAR include_consents AS BOOLEAN WITH "Include consent records?"
|
||||
data_scope = "custom"
|
||||
|
||||
CASE ELSE
|
||||
include_profile = TRUE
|
||||
include_conversations = TRUE
|
||||
include_files = TRUE
|
||||
include_activity = TRUE
|
||||
include_consents = TRUE
|
||||
data_scope = "complete"
|
||||
END SELECT
|
||||
|
||||
TALK ""
|
||||
TALK "🔄 Preparing your data export... This may take a few minutes."
|
||||
TALK ""
|
||||
|
||||
' Gather the data
|
||||
export_data = {}
|
||||
request_id = "EXP-" + FORMAT(NOW(), "YYYYMMDD-HHmmss") + "-" + user.id
|
||||
|
||||
' Export metadata
|
||||
export_data.metadata = {
|
||||
"export_id": request_id,
|
||||
"export_date": NOW(),
|
||||
"format": format_name,
|
||||
"data_scope": data_scope,
|
||||
"legal_basis": "LGPD Art. 18 V / GDPR Art. 20",
|
||||
"data_controller": "Your Organization Name",
|
||||
"contact": "privacy@company.com"
|
||||
}
|
||||
|
||||
' Gather profile data
|
||||
IF include_profile THEN
|
||||
profile = FIND "users" WHERE id = user.id
|
||||
export_data.profile = {
|
||||
"name": profile.name,
|
||||
"email": profile.email,
|
||||
"phone": profile.phone,
|
||||
"address": profile.address,
|
||||
"created_at": profile.created_at,
|
||||
"last_login": profile.last_login,
|
||||
"timezone": profile.timezone,
|
||||
"language": profile.language,
|
||||
"preferences": profile.preferences
|
||||
}
|
||||
TALK "✓ Profile data collected"
|
||||
END IF
|
||||
|
||||
' Gather conversations
|
||||
IF include_conversations THEN
|
||||
messages = FIND "messages" WHERE user_id = user.id ORDER BY created_at
|
||||
sessions = FIND "sessions" WHERE user_id = user.id
|
||||
|
||||
export_data.conversations = {
|
||||
"total_sessions": COUNT(sessions),
|
||||
"total_messages": COUNT(messages),
|
||||
"sessions": sessions,
|
||||
"messages": messages
|
||||
}
|
||||
TALK "✓ Conversation data collected (" + COUNT(messages) + " messages)"
|
||||
END IF
|
||||
|
||||
' Gather files metadata
|
||||
IF include_files THEN
|
||||
files = FIND "user_files" WHERE user_id = user.id
|
||||
|
||||
file_list = []
|
||||
FOR EACH file IN files
|
||||
file_info = {
|
||||
"filename": file.name,
|
||||
"size": file.size,
|
||||
"type": file.mime_type,
|
||||
"uploaded_at": file.created_at,
|
||||
"last_accessed": file.last_accessed,
|
||||
"path": file.path
|
||||
}
|
||||
APPEND file_list, file_info
|
||||
NEXT
|
||||
|
||||
export_data.files = {
|
||||
"total_files": COUNT(files),
|
||||
"total_size": SUM(files, "size"),
|
||||
"file_list": file_list
|
||||
}
|
||||
TALK "✓ Files metadata collected (" + COUNT(files) + " files)"
|
||||
END IF
|
||||
|
||||
' Gather activity logs
|
||||
IF include_activity THEN
|
||||
activity = FIND "activity_logs" WHERE user_id = user.id ORDER BY timestamp DESC LIMIT 10000
|
||||
|
||||
export_data.activity = {
|
||||
"total_events": COUNT(activity),
|
||||
"events": activity
|
||||
}
|
||||
TALK "✓ Activity logs collected (" + COUNT(activity) + " events)"
|
||||
END IF
|
||||
|
||||
' Gather consent records
|
||||
IF include_consents THEN
|
||||
consents = FIND "user_consents" WHERE user_id = user.id
|
||||
|
||||
export_data.consents = {
|
||||
"consent_records": consents,
|
||||
"current_preferences": {
|
||||
"marketing_emails": user.marketing_consent,
|
||||
"analytics": user.analytics_consent,
|
||||
"third_party_sharing": user.sharing_consent
|
||||
}
|
||||
}
|
||||
TALK "✓ Consent records collected"
|
||||
END IF
|
||||
|
||||
TALK ""
|
||||
TALK "📁 Generating export files..."
|
||||
|
||||
' Generate export files based on format
|
||||
timestamp = FORMAT(NOW(), "YYYYMMDD_HHmmss")
|
||||
base_filename = "data_export_" + timestamp
|
||||
|
||||
SELECT CASE export_format
|
||||
CASE "json"
|
||||
filename = base_filename + ".json"
|
||||
WRITE filename, JSON(export_data)
|
||||
|
||||
CASE "csv"
|
||||
' Generate multiple CSV files for different data types
|
||||
IF include_profile THEN
|
||||
WRITE base_filename + "_profile.csv", CSV(export_data.profile)
|
||||
END IF
|
||||
IF include_conversations THEN
|
||||
WRITE base_filename + "_messages.csv", CSV(export_data.conversations.messages)
|
||||
END IF
|
||||
IF include_files THEN
|
||||
WRITE base_filename + "_files.csv", CSV(export_data.files.file_list)
|
||||
END IF
|
||||
IF include_activity THEN
|
||||
WRITE base_filename + "_activity.csv", CSV(export_data.activity.events)
|
||||
END IF
|
||||
' Create ZIP of all CSVs
|
||||
filename = base_filename + "_csv.zip"
|
||||
COMPRESS filename, base_filename + "_*.csv"
|
||||
|
||||
CASE "xml"
|
||||
filename = base_filename + ".xml"
|
||||
WRITE filename, XML(export_data)
|
||||
|
||||
CASE "all"
|
||||
' Generate all formats
|
||||
WRITE base_filename + ".json", JSON(export_data)
|
||||
WRITE base_filename + ".xml", XML(export_data)
|
||||
|
||||
IF include_profile THEN
|
||||
WRITE base_filename + "_profile.csv", CSV(export_data.profile)
|
||||
END IF
|
||||
IF include_conversations THEN
|
||||
WRITE base_filename + "_messages.csv", CSV(export_data.conversations.messages)
|
||||
END IF
|
||||
IF include_files THEN
|
||||
WRITE base_filename + "_files.csv", CSV(export_data.files.file_list)
|
||||
END IF
|
||||
|
||||
filename = base_filename + "_complete.zip"
|
||||
COMPRESS filename, base_filename + ".*"
|
||||
END SELECT
|
||||
|
||||
' Upload to secure storage
|
||||
secure_path = "/secure/exports/" + user.id + "/"
|
||||
UPLOAD filename TO secure_path
|
||||
|
||||
' Generate download link (expires in 7 days)
|
||||
download_link = GENERATE SECURE LINK secure_path + filename EXPIRES 7 DAYS
|
||||
|
||||
' Log the export request for compliance
|
||||
INSERT INTO "privacy_requests" VALUES {
|
||||
"id": request_id,
|
||||
"user_id": user.id,
|
||||
"request_type": "data_portability",
|
||||
"data_scope": data_scope,
|
||||
"format": format_name,
|
||||
"requested_at": NOW(),
|
||||
"completed_at": NOW(),
|
||||
"status": "completed",
|
||||
"legal_basis": "LGPD Art. 18 V / GDPR Art. 20"
|
||||
}
|
||||
|
||||
' Send email with download link
|
||||
SEND MAIL email, "Your Data Export is Ready - " + request_id, "
|
||||
Dear " + user.name + ",
|
||||
|
||||
Your data export request has been completed.
|
||||
|
||||
**Export Details:**
|
||||
- Request ID: " + request_id + "
|
||||
- Format: " + format_name + "
|
||||
- Data Included: " + data_scope + "
|
||||
- Generated: " + FORMAT(NOW(), "DD/MM/YYYY HH:mm") + "
|
||||
|
||||
**Download Your Data:**
|
||||
" + download_link + "
|
||||
|
||||
⚠️ This link expires in 7 days for security purposes.
|
||||
|
||||
**What's Included:**
|
||||
" + IF(include_profile, "✓ Profile information\n", "") + IF(include_conversations, "✓ Conversation history\n", "") + IF(include_files, "✓ Files metadata\n", "") + IF(include_activity, "✓ Activity logs\n", "") + IF(include_consents, "✓ Consent records\n", "") + "
|
||||
|
||||
**Your Rights Under LGPD/GDPR:**
|
||||
- Import this data to another service provider
|
||||
- Request data deletion after export
|
||||
- Request additional data categories
|
||||
- File a complaint with data protection authorities
|
||||
|
||||
If you have questions, contact privacy@company.com
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
"
|
||||
|
||||
TALK ""
|
||||
TALK "✅ **Export Complete!**"
|
||||
TALK ""
|
||||
TALK "📧 A download link has been sent to: " + email
|
||||
TALK ""
|
||||
TALK "**Export Details:**"
|
||||
TALK "• Request ID: " + request_id
|
||||
TALK "• Format: " + format_name
|
||||
TALK "• Link expires in: 7 days"
|
||||
TALK ""
|
||||
TALK "You can use this data to:"
|
||||
TALK "• Import into another service"
|
||||
TALK "• Keep a personal backup"
|
||||
TALK "• Review what data we hold"
|
||||
TALK ""
|
||||
TALK "🔒 Need anything else?"
|
||||
TALK "• Say **'delete my data'** to request deletion"
|
||||
TALK "• Say **'privacy settings'** to manage consents"
|
||||
TALK "• Say **'help'** for other options"
|
||||
333
compliance/privacy.gbai/privacy.gbdialog/manage-consents.bas
Normal file
333
compliance/privacy.gbai/privacy.gbdialog/manage-consents.bas
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
' ============================================================================
|
||||
' Privacy Template: Consent Management
|
||||
' LGPD Art. 8 / GDPR Art. 7 - Consent Management
|
||||
' ============================================================================
|
||||
' This dialog allows users to view, grant, and revoke their consents
|
||||
' Essential for LGPD/GDPR compliance with granular consent tracking
|
||||
|
||||
TALK "🔐 **Consent Management Center**"
|
||||
TALK "Here you can view and manage all your data processing consents."
|
||||
TALK ""
|
||||
|
||||
' Verify user identity first
|
||||
HEAR email AS EMAIL WITH "Please enter your registered email address:"
|
||||
|
||||
user = FIND "users" WHERE email = email
|
||||
IF user IS NULL THEN
|
||||
TALK "⚠️ We couldn't find an account with that email."
|
||||
TALK "Please check the email address and try again."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Send quick verification
|
||||
code = GENERATE CODE 6
|
||||
SET SESSION "consent_verify_code", code
|
||||
SET SESSION "consent_verify_email", email
|
||||
|
||||
SEND MAIL email, "Consent Management - Verification", "
|
||||
Your verification code is: " + code + "
|
||||
|
||||
This code expires in 10 minutes.
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
"
|
||||
|
||||
HEAR entered_code AS TEXT WITH "📧 Enter the verification code sent to your email:"
|
||||
|
||||
IF entered_code <> code THEN
|
||||
TALK "❌ Invalid code. Please try again."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
TALK "✅ Identity verified!"
|
||||
TALK ""
|
||||
|
||||
' Load current consents
|
||||
consents = FIND "user_consents" WHERE user_id = user.id
|
||||
|
||||
' Define consent categories
|
||||
consent_categories = [
|
||||
{
|
||||
"id": "essential",
|
||||
"name": "Essential Services",
|
||||
"description": "Required for basic service functionality",
|
||||
"required": TRUE,
|
||||
"legal_basis": "Contract performance"
|
||||
},
|
||||
{
|
||||
"id": "analytics",
|
||||
"name": "Analytics & Improvement",
|
||||
"description": "Help us improve our services through usage analysis",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Legitimate interest / Consent"
|
||||
},
|
||||
{
|
||||
"id": "marketing",
|
||||
"name": "Marketing Communications",
|
||||
"description": "Receive news, updates, and promotional content",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Consent"
|
||||
},
|
||||
{
|
||||
"id": "personalization",
|
||||
"name": "Personalization",
|
||||
"description": "Customize your experience based on preferences",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Consent"
|
||||
},
|
||||
{
|
||||
"id": "third_party",
|
||||
"name": "Third-Party Sharing",
|
||||
"description": "Share data with trusted partners for enhanced services",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Consent"
|
||||
},
|
||||
{
|
||||
"id": "ai_training",
|
||||
"name": "AI Model Training",
|
||||
"description": "Use anonymized data to improve AI capabilities",
|
||||
"required": FALSE,
|
||||
"legal_basis": "Consent"
|
||||
}
|
||||
]
|
||||
|
||||
TALK "📋 **Your Current Consents:**"
|
||||
TALK ""
|
||||
|
||||
FOR EACH category IN consent_categories
|
||||
current_consent = FILTER(consents, "category = '" + category.id + "'")
|
||||
IF current_consent IS NOT NULL THEN
|
||||
status = current_consent.granted ? "✅ Granted" : "❌ Denied"
|
||||
granted_date = FORMAT(current_consent.updated_at, "DD/MM/YYYY")
|
||||
ELSE
|
||||
status = "⚪ Not Set"
|
||||
granted_date = "N/A"
|
||||
END IF
|
||||
|
||||
required_tag = category.required ? " (Required)" : ""
|
||||
TALK category.name + required_tag + ": " + status
|
||||
TALK " └─ " + category.description
|
||||
TALK " └─ Legal basis: " + category.legal_basis
|
||||
TALK " └─ Last updated: " + granted_date
|
||||
TALK ""
|
||||
NEXT
|
||||
|
||||
TALK "**What would you like to do?**"
|
||||
TALK ""
|
||||
TALK "1️⃣ Grant a consent"
|
||||
TALK "2️⃣ Revoke a consent"
|
||||
TALK "3️⃣ Revoke ALL optional consents"
|
||||
TALK "4️⃣ Grant ALL consents"
|
||||
TALK "5️⃣ View consent history"
|
||||
TALK "6️⃣ Download consent record"
|
||||
TALK "7️⃣ Exit"
|
||||
|
||||
HEAR action AS INTEGER WITH "Enter your choice (1-7):"
|
||||
|
||||
SELECT CASE action
|
||||
CASE 1
|
||||
' Grant consent
|
||||
TALK "Which consent would you like to grant?"
|
||||
TALK "Available options: analytics, marketing, personalization, third_party, ai_training"
|
||||
HEAR grant_category WITH "Enter consent category:"
|
||||
|
||||
' Validate category
|
||||
valid_categories = ["analytics", "marketing", "personalization", "third_party", "ai_training"]
|
||||
IF NOT CONTAINS(valid_categories, grant_category) THEN
|
||||
TALK "❌ Invalid category. Please try again."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Record consent with full audit trail
|
||||
consent_record = {
|
||||
"user_id": user.id,
|
||||
"category": grant_category,
|
||||
"granted": TRUE,
|
||||
"granted_at": NOW(),
|
||||
"updated_at": NOW(),
|
||||
"ip_address": GET SESSION "client_ip",
|
||||
"user_agent": GET SESSION "user_agent",
|
||||
"consent_version": "2.0",
|
||||
"method": "explicit_dialog"
|
||||
}
|
||||
|
||||
' Check if exists and update, otherwise insert
|
||||
existing = FIND "user_consents" WHERE user_id = user.id AND category = grant_category
|
||||
IF existing IS NOT NULL THEN
|
||||
UPDATE "user_consents" SET granted = TRUE, updated_at = NOW(), method = "explicit_dialog" WHERE id = existing.id
|
||||
ELSE
|
||||
INSERT INTO "user_consents" VALUES consent_record
|
||||
END IF
|
||||
|
||||
' Log to consent history
|
||||
INSERT INTO "consent_history" VALUES {
|
||||
"user_id": user.id,
|
||||
"category": grant_category,
|
||||
"action": "granted",
|
||||
"timestamp": NOW(),
|
||||
"ip_address": GET SESSION "client_ip"
|
||||
}
|
||||
|
||||
TALK "✅ Consent for **" + grant_category + "** has been granted."
|
||||
TALK "You can revoke this consent at any time."
|
||||
|
||||
CASE 2
|
||||
' Revoke consent
|
||||
TALK "Which consent would you like to revoke?"
|
||||
TALK "Note: Essential services consent cannot be revoked while using the service."
|
||||
HEAR revoke_category WITH "Enter consent category:"
|
||||
|
||||
IF revoke_category = "essential" THEN
|
||||
TALK "⚠️ Essential consent is required for service operation."
|
||||
TALK "To revoke it, you must delete your account."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
UPDATE "user_consents" SET granted = FALSE, updated_at = NOW(), method = "explicit_revoke" WHERE user_id = user.id AND category = revoke_category
|
||||
|
||||
INSERT INTO "consent_history" VALUES {
|
||||
"user_id": user.id,
|
||||
"category": revoke_category,
|
||||
"action": "revoked",
|
||||
"timestamp": NOW(),
|
||||
"ip_address": GET SESSION "client_ip"
|
||||
}
|
||||
|
||||
TALK "✅ Consent for **" + revoke_category + "** has been revoked."
|
||||
TALK "This change takes effect immediately."
|
||||
|
||||
' Notify relevant systems
|
||||
WEBHOOK POST "/internal/consent-changed" WITH {
|
||||
"user_id": user.id,
|
||||
"category": revoke_category,
|
||||
"action": "revoked"
|
||||
}
|
||||
|
||||
CASE 3
|
||||
' Revoke all optional
|
||||
TALK "⚠️ This will revoke ALL optional consents:"
|
||||
TALK "• Analytics & Improvement"
|
||||
TALK "• Marketing Communications"
|
||||
TALK "• Personalization"
|
||||
TALK "• Third-Party Sharing"
|
||||
TALK "• AI Model Training"
|
||||
|
||||
HEAR confirm WITH "Type 'REVOKE ALL' to confirm:"
|
||||
|
||||
IF confirm <> "REVOKE ALL" THEN
|
||||
TALK "Operation cancelled."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
UPDATE "user_consents" SET granted = FALSE, updated_at = NOW() WHERE user_id = user.id AND category <> "essential"
|
||||
|
||||
INSERT INTO "consent_history" VALUES {
|
||||
"user_id": user.id,
|
||||
"category": "ALL_OPTIONAL",
|
||||
"action": "bulk_revoked",
|
||||
"timestamp": NOW(),
|
||||
"ip_address": GET SESSION "client_ip"
|
||||
}
|
||||
|
||||
TALK "✅ All optional consents have been revoked."
|
||||
|
||||
CASE 4
|
||||
' Grant all
|
||||
TALK "This will grant consent for all categories."
|
||||
TALK "You can revoke individual consents at any time."
|
||||
|
||||
HEAR confirm WITH "Type 'GRANT ALL' to confirm:"
|
||||
|
||||
IF confirm <> "GRANT ALL" THEN
|
||||
TALK "Operation cancelled."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
FOR EACH category IN consent_categories
|
||||
existing = FIND "user_consents" WHERE user_id = user.id AND category = category.id
|
||||
IF existing IS NOT NULL THEN
|
||||
UPDATE "user_consents" SET granted = TRUE, updated_at = NOW() WHERE id = existing.id
|
||||
ELSE
|
||||
INSERT INTO "user_consents" VALUES {
|
||||
"user_id": user.id,
|
||||
"category": category.id,
|
||||
"granted": TRUE,
|
||||
"granted_at": NOW(),
|
||||
"updated_at": NOW(),
|
||||
"method": "bulk_grant"
|
||||
}
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
INSERT INTO "consent_history" VALUES {
|
||||
"user_id": user.id,
|
||||
"category": "ALL",
|
||||
"action": "bulk_granted",
|
||||
"timestamp": NOW()
|
||||
}
|
||||
|
||||
TALK "✅ All consents have been granted."
|
||||
|
||||
CASE 5
|
||||
' View history
|
||||
TALK "📜 **Your Consent History:**"
|
||||
TALK ""
|
||||
|
||||
history = FIND "consent_history" WHERE user_id = user.id ORDER BY timestamp DESC LIMIT 20
|
||||
|
||||
IF COUNT(history) = 0 THEN
|
||||
TALK "No consent history found."
|
||||
ELSE
|
||||
FOR EACH record IN history
|
||||
action_icon = record.action CONTAINS "grant" ? "✅" : "❌"
|
||||
TALK action_icon + " " + FORMAT(record.timestamp, "DD/MM/YYYY HH:mm") + " - " + record.category + " " + record.action
|
||||
NEXT
|
||||
END IF
|
||||
|
||||
CASE 6
|
||||
' Download consent record
|
||||
TALK "📥 Generating your consent record..."
|
||||
|
||||
consent_report = {
|
||||
"generated_at": NOW(),
|
||||
"user_email": email,
|
||||
"current_consents": consents,
|
||||
"consent_history": FIND "consent_history" WHERE user_id = user.id,
|
||||
"legal_notice": "This document serves as proof of consent status under LGPD/GDPR"
|
||||
}
|
||||
|
||||
filename = "consent_record_" + FORMAT(NOW(), "YYYYMMDD") + ".pdf"
|
||||
GENERATE PDF filename WITH TEMPLATE "consent_report" DATA consent_report
|
||||
|
||||
SEND MAIL email, "Your Consent Record", "
|
||||
Dear User,
|
||||
|
||||
Please find attached your complete consent record as requested.
|
||||
|
||||
This document includes:
|
||||
- Current consent status for all categories
|
||||
- Complete consent history with timestamps
|
||||
- Legal basis for each processing activity
|
||||
|
||||
Keep this document for your records.
|
||||
|
||||
Pragmatismo Privacy Team
|
||||
", ATTACHMENT filename
|
||||
|
||||
TALK "✅ Consent record has been sent to " + email
|
||||
|
||||
CASE 7
|
||||
TALK "Thank you for managing your privacy preferences."
|
||||
TALK "You can return here anytime to update your consents."
|
||||
EXIT
|
||||
|
||||
CASE ELSE
|
||||
TALK "Invalid choice. Please try again."
|
||||
END SELECT
|
||||
|
||||
TALK ""
|
||||
TALK "🔒 **Privacy Reminder:**"
|
||||
TALK "• Your consents are stored securely"
|
||||
TALK "• Changes take effect immediately"
|
||||
TALK "• You can modify consents anytime"
|
||||
TALK "• Contact privacy@company.com for questions"
|
||||
152
compliance/privacy.gbai/privacy.gbdialog/request-data.bas
Normal file
152
compliance/privacy.gbai/privacy.gbdialog/request-data.bas
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
' ============================================================================
|
||||
' Privacy Template: Data Access Request (Subject Access Request - SAR)
|
||||
' LGPD Art. 18 / GDPR Art. 15 - Right of Access
|
||||
' ============================================================================
|
||||
' This dialog handles user requests to access their personal data
|
||||
' Companies can install this template for LGPD/GDPR compliance
|
||||
|
||||
TALK "Data Access Request"
|
||||
TALK "You have the right to access all personal data we hold about you."
|
||||
TALK ""
|
||||
|
||||
' Verify user identity
|
||||
TALK "First, I need to verify your identity for security purposes."
|
||||
HEAR email AS EMAIL WITH "Please provide your registered email address:"
|
||||
|
||||
' Check if email exists in system
|
||||
user = FIND "users" WHERE email = email
|
||||
IF user IS NULL THEN
|
||||
TALK "We couldn't find an account with that email address."
|
||||
TALK "Please check the email and try again, or contact support."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
' Send verification code
|
||||
code = GENERATE CODE 6
|
||||
SET SESSION "verification_code", code
|
||||
SET SESSION "verified_email", email
|
||||
|
||||
SEND MAIL email, "Data Access Request - Verification Code", "
|
||||
Your verification code is: " + code + "
|
||||
|
||||
This code expires in 15 minutes.
|
||||
|
||||
If you did not request this, please ignore this email.
|
||||
"
|
||||
|
||||
HEAR entered_code AS TEXT WITH "We sent a verification code to your email. Please enter it:"
|
||||
|
||||
IF entered_code <> code THEN
|
||||
TALK "Invalid verification code. Please start over."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
TALK "Identity verified successfully!"
|
||||
TALK ""
|
||||
|
||||
' Gather all user data
|
||||
TALK "Gathering your personal data... This may take a moment."
|
||||
TALK ""
|
||||
|
||||
' Get user profile data
|
||||
profile = FIND "users" WHERE email = email
|
||||
sessions = FIND "sessions" WHERE user_id = profile.id
|
||||
messages = FIND "messages" WHERE user_id = profile.id
|
||||
files = FIND "user_files" WHERE user_id = profile.id
|
||||
consents = FIND "user_consents" WHERE user_id = profile.id
|
||||
audit_logs = FIND "audit_logs" WHERE user_id = profile.id
|
||||
|
||||
' Build comprehensive report
|
||||
report_data = {
|
||||
"request_date": NOW(),
|
||||
"request_type": "Subject Access Request (SAR)",
|
||||
"legal_basis": "LGPD Art. 18 / GDPR Art. 15",
|
||||
"profile": {
|
||||
"name": profile.name,
|
||||
"email": profile.email,
|
||||
"phone": profile.phone,
|
||||
"created_at": profile.created_at,
|
||||
"last_login": profile.last_login,
|
||||
"preferences": profile.preferences
|
||||
},
|
||||
"sessions": {
|
||||
"total_count": COUNT(sessions),
|
||||
"active_sessions": FILTER(sessions, "status = 'active'"),
|
||||
"session_history": sessions
|
||||
},
|
||||
"communications": {
|
||||
"total_messages": COUNT(messages),
|
||||
"messages": messages
|
||||
},
|
||||
"files": {
|
||||
"total_files": COUNT(files),
|
||||
"file_list": MAP(files, "name, size, created_at")
|
||||
},
|
||||
"consents": consents,
|
||||
"activity_log": audit_logs
|
||||
}
|
||||
|
||||
' Generate PDF report
|
||||
report_filename = "data_access_report_" + FORMAT(NOW(), "YYYYMMDD_HHmmss") + ".pdf"
|
||||
GENERATE PDF report_filename WITH TEMPLATE "data_access_report" DATA report_data
|
||||
|
||||
' Upload to user's secure area
|
||||
UPLOAD report_filename TO "/secure/reports/" + profile.id + "/"
|
||||
|
||||
' Send report via email
|
||||
SEND MAIL email, "Your Data Access Request - Complete Report", "
|
||||
Dear " + profile.name + ",
|
||||
|
||||
As requested, please find attached a complete report of all personal data we hold about you.
|
||||
|
||||
This report includes:
|
||||
- Your profile information
|
||||
- Session history
|
||||
- Communication records
|
||||
- Files you have uploaded
|
||||
- Consent records
|
||||
- Activity logs
|
||||
|
||||
Report generated: " + FORMAT(NOW(), "DD/MM/YYYY HH:mm") + "
|
||||
|
||||
Your rights under LGPD/GDPR:
|
||||
- Right to rectification (Art. 18 III LGPD / Art. 16 GDPR)
|
||||
- Right to erasure (Art. 18 VI LGPD / Art. 17 GDPR)
|
||||
- Right to data portability (Art. 18 V LGPD / Art. 20 GDPR)
|
||||
- Right to object to processing (Art. 18 IV LGPD / Art. 21 GDPR)
|
||||
|
||||
To exercise any of these rights, please contact us or use our privacy portal.
|
||||
|
||||
Best regards,
|
||||
Privacy & Compliance Team
|
||||
", ATTACHMENT report_filename
|
||||
|
||||
' Log the request for compliance audit
|
||||
INSERT INTO "privacy_requests" VALUES {
|
||||
"user_id": profile.id,
|
||||
"request_type": "data_access",
|
||||
"requested_at": NOW(),
|
||||
"completed_at": NOW(),
|
||||
"status": "completed",
|
||||
"legal_basis": "LGPD Art. 18 / GDPR Art. 15"
|
||||
}
|
||||
|
||||
TALK "Request Complete!"
|
||||
TALK ""
|
||||
TALK "We have sent a comprehensive report to: " + email
|
||||
TALK ""
|
||||
TALK "The report includes:"
|
||||
TALK "- Your profile information"
|
||||
TALK "- " + COUNT(sessions) + " session records"
|
||||
TALK "- " + COUNT(messages) + " message records"
|
||||
TALK "- " + COUNT(files) + " files"
|
||||
TALK "- Consent history"
|
||||
TALK "- Activity logs"
|
||||
TALK ""
|
||||
TALK "You can also download the report from your account settings."
|
||||
TALK ""
|
||||
TALK "Your other privacy rights:"
|
||||
TALK "- Say 'correct my data' to update your information"
|
||||
TALK "- Say 'delete my data' to request data erasure"
|
||||
TALK "- Say 'export my data' for portable format"
|
||||
TALK "- Say 'privacy settings' to manage consents"
|
||||
51
compliance/privacy.gbai/privacy.gbdialog/start.bas
Normal file
51
compliance/privacy.gbai/privacy.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
ADD TOOL "request-data"
|
||||
ADD TOOL "export-data"
|
||||
ADD TOOL "delete-data"
|
||||
ADD TOOL "manage-consents"
|
||||
ADD TOOL "rectify-data"
|
||||
ADD TOOL "object-processing"
|
||||
|
||||
USE KB "privacy.gbkb"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "access" AS "View my data"
|
||||
ADD SUGGESTION "export" AS "Export my data"
|
||||
ADD SUGGESTION "delete" AS "Delete my data"
|
||||
ADD SUGGESTION "consents" AS "Manage consents"
|
||||
ADD SUGGESTION "correct" AS "Correct my data"
|
||||
ADD SUGGESTION "object" AS "Object to processing"
|
||||
|
||||
SET CONTEXT "privacy rights" AS "You are a Privacy Rights Center assistant helping users exercise their data protection rights under LGPD, GDPR, and CCPA. Help with data access, rectification, erasure, portability, and consent management."
|
||||
|
||||
BEGIN TALK
|
||||
**Privacy Rights Center**
|
||||
|
||||
As a data subject, you have the following rights:
|
||||
|
||||
1. **Access** - View all data we hold about you
|
||||
2. **Rectification** - Correct inaccurate data
|
||||
3. **Erasure** - Request deletion of your data
|
||||
4. **Portability** - Export your data
|
||||
5. **Object** - Opt-out of certain processing
|
||||
6. **Consent** - Review and update your consents
|
||||
|
||||
Select an option or describe your request.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a Privacy Rights Center assistant for LGPD/GDPR/CCPA compliance.
|
||||
|
||||
Data subject rights:
|
||||
- Right of Access: View all personal data
|
||||
- Right to Rectification: Correct inaccurate data
|
||||
- Right to Erasure: Delete personal data (right to be forgotten)
|
||||
- Right to Portability: Export data in machine-readable format
|
||||
- Right to Object: Opt-out of marketing, profiling, etc.
|
||||
- Consent Management: Review and withdraw consents
|
||||
|
||||
Always verify identity before processing sensitive requests.
|
||||
Log all privacy requests for compliance audit.
|
||||
Provide clear timelines for request fulfillment.
|
||||
Escalate complex requests to the Data Protection Officer.
|
||||
END SYSTEM PROMPT
|
||||
44
compliance/privacy.gbai/privacy.gbot/config.csv
Normal file
44
compliance/privacy.gbai/privacy.gbot/config.csv
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
name,value
|
||||
Bot Name,Privacy Rights Center
|
||||
Bot Description,LGPD/GDPR Data Subject Rights Management Bot
|
||||
Bot Version,1.0.0
|
||||
Bot Author,Pragmatismo
|
||||
Bot License,AGPL-3.0
|
||||
Bot Category,Compliance
|
||||
Bot Tags,privacy;lgpd;gdpr;hipaa;ccpa;compliance;data-protection
|
||||
Default Language,en
|
||||
Supported Languages,en;pt;es;de;fr;it
|
||||
Welcome Message,Welcome to the Privacy Rights Center. I can help you exercise your data protection rights under LGPD, GDPR, and other privacy regulations.
|
||||
Error Message,I apologize, but I encountered an issue processing your request. Please try again or contact our privacy team at privacy@company.com
|
||||
Timeout Message,Your session has timed out for security. Please start a new conversation.
|
||||
Session Timeout,900
|
||||
Max Retries,3
|
||||
Log Level,info
|
||||
Enable Audit Log,true
|
||||
Audit Log Retention Days,2555
|
||||
Require Authentication,true
|
||||
Require Email Verification,true
|
||||
Require 2FA,false
|
||||
Data Retention Days,30
|
||||
Auto Delete Completed Requests,false
|
||||
Send Confirmation Emails,true
|
||||
Privacy Officer Email,privacy@company.com
|
||||
DPO Contact,dpo@company.com
|
||||
Supervisory Authority,ANPD
|
||||
Company Name,Your Company Name
|
||||
Company Address,Your Company Address
|
||||
Company Country,BR
|
||||
Compliance Frameworks,LGPD;GDPR;CCPA
|
||||
Response SLA Hours,72
|
||||
Escalation Email,legal@company.com
|
||||
Enable HIPAA Mode,false
|
||||
PHI Processing Enabled,false
|
||||
Encryption At Rest,true
|
||||
Encryption In Transit,true
|
||||
PII Detection Enabled,true
|
||||
Auto Anonymization,true
|
||||
Consent Required,true
|
||||
Consent Version,1.0
|
||||
Terms URL,https://company.com/terms
|
||||
Privacy Policy URL,https://company.com/privacy
|
||||
Cookie Policy URL,https://company.com/cookies
|
||||
|
Can't render this file because it has a wrong number of fields in line 11.
|
913
compliance/privacy.gbai/privacy.gbui/index.html
Normal file
913
compliance/privacy.gbai/privacy.gbui/index.html
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Privacy Rights Center</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #2563eb;
|
||||
--primary-hover: #1d4ed8;
|
||||
--success-color: #10b981;
|
||||
--warning-color: #f59e0b;
|
||||
--danger-color: #ef4444;
|
||||
--text-primary: #1e293b;
|
||||
--text-secondary: #64748b;
|
||||
--bg-primary: #f8fafc;
|
||||
--bg-secondary: #ffffff;
|
||||
--border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 2rem;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.compliance-badges {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.badge.lgpd { border-color: #22c55e; color: #16a34a; }
|
||||
.badge.gdpr { border-color: #3b82f6; color: #2563eb; }
|
||||
.badge.hipaa { border-color: #a855f7; color: #9333ea; }
|
||||
.badge.ccpa { border-color: #f97316; color: #ea580c; }
|
||||
|
||||
.rights-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.right-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.right-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.right-card .icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.right-card h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.right-card p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.right-card .legal-ref {
|
||||
font-size: 0.75rem;
|
||||
color: var(--primary-color);
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-overlay.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 1rem;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary-color);
|
||||
border: 1px solid rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: #b45309;
|
||||
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #047857;
|
||||
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.consent-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: var(--bg-primary);
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.consent-info h4 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.consent-info p {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #cbd5e1;
|
||||
transition: 0.3s;
|
||||
border-radius: 26px;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider {
|
||||
background: var(--success-color);
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider:before {
|
||||
transform: translateX(22px);
|
||||
}
|
||||
|
||||
.request-status {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.status-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.status-list {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr 1fr 1fr;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.rights-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="logo">🔒</div>
|
||||
<h1>Privacy Rights Center</h1>
|
||||
<p class="subtitle">Exercise your data protection rights under LGPD, GDPR, and other privacy regulations</p>
|
||||
<div class="compliance-badges">
|
||||
<span class="badge lgpd">🇧🇷 LGPD Compliant</span>
|
||||
<span class="badge gdpr">🇪🇺 GDPR Compliant</span>
|
||||
<span class="badge hipaa">🏥 HIPAA Ready</span>
|
||||
<span class="badge ccpa">🇺🇸 CCPA Compliant</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="rights-grid">
|
||||
<div class="right-card" onclick="openModal('accessModal')">
|
||||
<div class="icon">📋</div>
|
||||
<h3>Access My Data</h3>
|
||||
<p>Request a complete copy of all personal data we hold about you in a portable format.</p>
|
||||
<span class="legal-ref">LGPD Art. 18 / GDPR Art. 15</span>
|
||||
</div>
|
||||
|
||||
<div class="right-card" onclick="openModal('rectifyModal')">
|
||||
<div class="icon">✏️</div>
|
||||
<h3>Correct My Data</h3>
|
||||
<p>Request correction of inaccurate or incomplete personal data we hold about you.</p>
|
||||
<span class="legal-ref">LGPD Art. 18 III / GDPR Art. 16</span>
|
||||
</div>
|
||||
|
||||
<div class="right-card" onclick="openModal('deleteModal')">
|
||||
<div class="icon">🗑️</div>
|
||||
<h3>Delete My Data</h3>
|
||||
<p>Request deletion of your personal data (Right to be Forgotten).</p>
|
||||
<span class="legal-ref">LGPD Art. 18 VI / GDPR Art. 17</span>
|
||||
</div>
|
||||
|
||||
<div class="right-card" onclick="openModal('portabilityModal')">
|
||||
<div class="icon">📦</div>
|
||||
<h3>Export My Data</h3>
|
||||
<p>Download your data in a machine-readable format to transfer to another service.</p>
|
||||
<span class="legal-ref">LGPD Art. 18 V / GDPR Art. 20</span>
|
||||
</div>
|
||||
|
||||
<div class="right-card" onclick="openModal('consentModal')">
|
||||
<div class="icon">⚙️</div>
|
||||
<h3>Manage Consents</h3>
|
||||
<p>Review and update your data processing consents and preferences.</p>
|
||||
<span class="legal-ref">LGPD Art. 8 / GDPR Art. 7</span>
|
||||
</div>
|
||||
|
||||
<div class="right-card" onclick="openModal('objectModal')">
|
||||
<div class="icon">🚫</div>
|
||||
<h3>Object to Processing</h3>
|
||||
<p>Object to certain types of data processing or opt-out of specific activities.</p>
|
||||
<span class="legal-ref">LGPD Art. 18 IV / GDPR Art. 21</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="request-status">
|
||||
<div class="status-header">
|
||||
<h2>📊 Your Request History</h2>
|
||||
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
||||
</div>
|
||||
<div class="status-list" id="statusList">
|
||||
<div class="status-item">
|
||||
<span class="status-badge status-completed">Completed</span>
|
||||
<span>Data Access Request</span>
|
||||
<span>2025-01-15</span>
|
||||
<a href="#" class="btn btn-secondary" style="padding: 0.5rem 1rem; font-size: 0.875rem;">Download</a>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-badge status-processing">Processing</span>
|
||||
<span>Consent Update</span>
|
||||
<span>2025-01-20</span>
|
||||
<span>In Progress</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Access Data Modal -->
|
||||
<div class="modal-overlay" id="accessModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('accessModal')">×</button>
|
||||
<h2>📋 Access My Data</h2>
|
||||
<div class="alert alert-info">
|
||||
<span>ℹ️</span>
|
||||
<span>You will receive a complete report of all personal data we hold about you within 15 days.</span>
|
||||
</div>
|
||||
<form onsubmit="submitRequest(event, 'access')">
|
||||
<div class="form-group">
|
||||
<label for="access</span>-email">Email Address *</label>
|
||||
<input type="email" id="access-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="access-format">Preferred Format</label>
|
||||
<select id="access-format">
|
||||
<option value="pdf">PDF Report</option>
|
||||
<option value="json">JSON (Machine Readable)</option>
|
||||
<option value="csv">CSV (Spreadsheet)</option>
|
||||
<option value="all">All Formats</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="access-notes">Additional Notes</label>
|
||||
<textarea id="access-notes" placeholder="Any specific data you're looking for..."></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">Submit Request</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Data Modal -->
|
||||
<div class="modal-overlay" id="deleteModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('deleteModal')">×</button>
|
||||
<h2>🗑️ Delete My Data</h2>
|
||||
<div class="alert alert-warning">
|
||||
<span>⚠️</span>
|
||||
<span>This action is permanent and cannot be undone. Some data may be retained for legal compliance.</span>
|
||||
</div>
|
||||
<form onsubmit="submitRequest(event, 'delete')">
|
||||
<div class="form-group">
|
||||
<label for="delete-email">Email Address *</label>
|
||||
<input type="email" id="delete-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="delete-scope">What to Delete</label>
|
||||
<select id="delete-scope">
|
||||
<option value="all">Everything (Complete Account Deletion)</option>
|
||||
<option value="conversations">Conversation History Only</option>
|
||||
<option value="files">Files and Documents Only</option>
|
||||
<option value="activity">Activity Logs Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="delete-reason">Reason (Optional)</label>
|
||||
<textarea id="delete-reason" placeholder="Help us improve by sharing why you're leaving..."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="delete-confirm" required>
|
||||
<label for="delete-confirm">I understand this action is permanent and I want to proceed with data deletion</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger" style="width: 100%;">Request Deletion</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Consent Management Modal -->
|
||||
<div class="modal-overlay" id="consentModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('consentModal')">×</button>
|
||||
<h2>⚙️ Manage Consents</h2>
|
||||
<div class="alert alert-info">
|
||||
<span>ℹ️</span>
|
||||
<span>Changes to your consents take effect immediately.</span>
|
||||
</div>
|
||||
<form onsubmit="submitRequest(event, 'consent')">
|
||||
<div class="form-group">
|
||||
<label for="consent-email">Email Address *</label>
|
||||
<input type="email" id="consent-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>Essential Services</h4>
|
||||
<p>Required for basic functionality (cannot be disabled)</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" checked disabled>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>Analytics & Improvement</h4>
|
||||
<p>Help us improve through usage analysis</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="consent-analytics" checked>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>Marketing Communications</h4>
|
||||
<p>Receive news, updates, and promotions</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="consent-marketing">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>Personalization</h4>
|
||||
<p>Customize experience based on your usage</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="consent-personalization" checked>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>Third-Party Sharing</h4>
|
||||
<p>Share data with trusted partners</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="consent-thirdparty">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="consent-item">
|
||||
<div class="consent-info">
|
||||
<h4>AI Model Training</h4>
|
||||
<p>Use anonymized data to improve AI</p>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="consent-ai">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 1rem;">Save Preferences</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rectify Modal -->
|
||||
<div class="modal-overlay" id="rectifyModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('rectifyModal')">×</button>
|
||||
<h2>✏️ Correct My Data</h2>
|
||||
<form onsubmit="submitRequest(event, 'rectify')">
|
||||
<div class="form-group">
|
||||
<label for="rectify-email">Email Address *</label>
|
||||
<input type="email" id="rectify-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rectify-field">Data to Correct *</label>
|
||||
<select id="rectify-field" required>
|
||||
<option value="">Select field...</option>
|
||||
<option value="name">Name</option>
|
||||
<option value="email">Email Address</option>
|
||||
<option value="phone">Phone Number</option>
|
||||
<option value="address">Address</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rectify-current">Current Value</label>
|
||||
<input type="text" id="rectify-current" placeholder="What it currently shows">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rectify-new">Correct Value *</label>
|
||||
<input type="text" id="rectify-new" required placeholder="What it should be">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rectify-reason">Additional Information</label>
|
||||
<textarea id="rectify-reason" placeholder="Any additional details..."></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">Submit Correction Request</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Portability Modal -->
|
||||
<div class="modal-overlay" id="portabilityModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('portabilityModal')">×</button>
|
||||
<h2>📦 Export My Data</h2>
|
||||
<div class="alert alert-info">
|
||||
<span>ℹ️</span>
|
||||
<span>Your data will be prepared and a download link sent to your email within 72 hours.</span>
|
||||
</div>
|
||||
<form onsubmit="submitRequest(event, 'portability')">
|
||||
<div class="form-group">
|
||||
<label for="port-email">Email Address *</label>
|
||||
<input type="email" id="port-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="port-format">Export Format *</label>
|
||||
<select id="port-format" required>
|
||||
<option value="json">JSON (Recommended for data transfer)</option>
|
||||
<option value="csv">CSV (For spreadsheets)</option>
|
||||
<option value="xml">XML (Universal format)</option>
|
||||
<option value="all">All Formats (ZIP archive)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Data Categories to Include</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="port-profile" checked>
|
||||
<label for="port-profile">Profile Information</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="port-conversations" checked>
|
||||
<label for="port-conversations">Conversations & Messages</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="port-files" checked>
|
||||
<label for="port-files">Files & Documents</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="port-activity">
|
||||
<label for="port-activity">Activity Logs</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="port-consents" checked>
|
||||
<label for="port-consents">Consent Records</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">Generate Export</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Object Modal -->
|
||||
<div class="modal-overlay" id="objectModal">
|
||||
<div class="modal">
|
||||
<button class="modal-close" onclick="closeModal('objectModal')">×</button>
|
||||
<h2>🚫 Object to Processing</h2>
|
||||
<form onsubmit="submitRequest(event, 'object')">
|
||||
<div class="form-group">
|
||||
<label for="object-email">Email Address *</label>
|
||||
<input type="email" id="object-email" required placeholder="your@email.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="object-type">Processing Activity *</label>
|
||||
<select id="object-type" required>
|
||||
<option value="">Select activity...</option>
|
||||
<option value="profiling">Automated Profiling</option>
|
||||
<option value="direct-marketing">Direct Marketing</option>
|
||||
<option value="analytics">Analytics & Statistics</option>
|
||||
<option value="third-party">Third-Party Data Sharing</option>
|
||||
<option value="ai-processing">AI/ML Processing</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="object-reason">Reason for Objection *</label>
|
||||
<textarea id="object-reason" required placeholder="Please explain why you object to this processing..."></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">Submit Objection</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>🔒 Your privacy matters. All requests are processed securely and confidentially.</p>
|
||||
<p>
|
||||
<a href="/privacy-policy">Privacy Policy</a> •
|
||||
<a href="/terms">Terms of Service</a> •
|
||||
<a href="mailto:privacy@company.com">Contact DPO</a>
|
||||
</p>
|
||||
<p style="margin-top: 1rem;">© 2025 Pragmatismo. Built with General Bots.</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function openModal(modalId) {
|
||||
document.getElementById(modalId).classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeModal(modalId) {
|
||||
document.getElementById(modalId).classList.remove('active');
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
|
||||
// Close modal on overlay click
|
||||
document.querySelectorAll('.modal-overlay').forEach(overlay => {
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) {
|
||||
overlay.classList.remove('active');
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close modal on Escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
document.querySelectorAll('.modal-overlay.active').forEach(modal => {
|
||||
modal.classList.remove('active');
|
||||
});
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
});
|
||||
|
||||
async function submitRequest(event, type) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
data.request_type = type;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/privacy/request', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('✅ Your request has been submitted successfully! Check your email for confirmation.');
|
||||
form.reset();
|
||||
closeModal(form.closest('.modal-overlay').id);
|
||||
refreshStatus();
|
||||
} else {
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
} catch (error) {
|
||||
alert('❌ There was an error submitting your request. Please try again or contact support.');
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/privacy/requests');
|
||||
if (response.ok) {
|
||||
const requests = await response.json();
|
||||
updateStatusList(requests);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusList(requests) {
|
||||
const statusList = document.getElementById('statusList');
|
||||
if (requests.length === 0) {
|
||||
statusList.innerHTML = '<p style="padding: 1rem; color: var(--text-secondary);">No requests found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
statusList.innerHTML = requests.map(req => `
|
||||
<div class="status-item">
|
||||
<span class="status-badge status-${req.status.toLowerCase()}">${req.status}</span>
|
||||
<span>${req.type}</span>
|
||||
<span>${new Date(req.created_at).toLocaleDateString()}</span>
|
||||
${req.download_url ?
|
||||
`<a href="${req.download_url}" class="btn btn-secondary" style="padding: 0.5rem 1rem; font-size: 0.875rem;">Download</a>` :
|
||||
`<span>${req.status}</span>`
|
||||
}
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Load status on page load
|
||||
document.addEventListener('DOMContentLoaded', refreshStatus);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
275
crawler.gbai/README.md
Normal file
275
crawler.gbai/README.md
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
# Web Crawler Template (crawler.gbai)
|
||||
|
||||
A General Bots template for automated web crawling and content extraction for knowledge base population.
|
||||
|
||||
## Overview
|
||||
|
||||
The Crawler template enables your bot to automatically fetch, parse, and index web content. It's designed for building knowledge bases from websites, monitoring web pages for changes, and extracting structured data from online sources.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automated Web Scraping** - Fetch and parse web pages automatically
|
||||
- **Document Mode** - Answer questions based on crawled content
|
||||
- **Configurable Depth** - Control how many pages to crawl
|
||||
- **Content Indexing** - Automatically add content to knowledge base
|
||||
- **LLM Integration** - Use AI to understand and summarize crawled content
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
crawler.gbai/
|
||||
├── README.md
|
||||
├── crawler.gbkb/ # Knowledge base for crawled content
|
||||
│ └── docs/ # Indexed documents
|
||||
└── crawler.gbot/
|
||||
└── config.csv # Crawler configuration
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure the crawler in `crawler.gbot/config.csv`:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `Website` | Target URL to crawl | `https://pragmatismo.com.br/` |
|
||||
| `website Max Documents` | Maximum pages to crawl | `2` |
|
||||
| `Answer Mode` | How to respond to queries | `document` |
|
||||
| `Theme Color` | UI theme color | `purple` |
|
||||
| `LLM Provider` | AI provider for processing | `openai` |
|
||||
|
||||
### Example config.csv
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Website,https://pragmatismo.com.br/
|
||||
website Max Documents,2
|
||||
Answer Mode,document
|
||||
Theme Color,purple
|
||||
LLM Provider,openai
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Initialization** - Bot reads the target website from configuration
|
||||
2. **Crawling** - Fetches pages starting from the root URL
|
||||
3. **Extraction** - Parses HTML and extracts meaningful text content
|
||||
4. **Indexing** - Stores content in the knowledge base for RAG
|
||||
5. **Q&A** - Users can ask questions about the crawled content
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup
|
||||
|
||||
1. Copy the template to your bot's packages directory:
|
||||
|
||||
```bash
|
||||
cp -r templates/crawler.gbai /path/to/your/bot/packages/
|
||||
```
|
||||
|
||||
2. Edit `crawler.gbot/config.csv` with your target website:
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Website,https://your-website.com/
|
||||
website Max Documents,10
|
||||
Answer Mode,document
|
||||
```
|
||||
|
||||
3. Deploy and the bot will automatically crawl the configured site.
|
||||
|
||||
### Querying Crawled Content
|
||||
|
||||
Once crawled, users can ask questions naturally:
|
||||
|
||||
- "What services does the company offer?"
|
||||
- "Tell me about the pricing"
|
||||
- "Summarize the about page"
|
||||
- "What are the main features?"
|
||||
|
||||
### Answer Modes
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `document` | Answers strictly based on crawled content |
|
||||
| `hybrid` | Combines crawled content with general knowledge |
|
||||
| `summary` | Provides concise summaries of relevant pages |
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Limiting Crawl Scope
|
||||
|
||||
Control which pages are crawled:
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Website,https://example.com/docs/
|
||||
website Max Documents,50
|
||||
Website Include Pattern,/docs/*
|
||||
Website Exclude Pattern,/docs/archive/*
|
||||
```
|
||||
|
||||
### Scheduling Recrawls
|
||||
|
||||
Set up periodic recrawling to keep content fresh:
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Website Refresh Schedule,0 0 * * 0
|
||||
```
|
||||
|
||||
This example recrawls every Sunday at midnight.
|
||||
|
||||
### Authentication
|
||||
|
||||
For sites requiring authentication:
|
||||
|
||||
```csv
|
||||
name,value
|
||||
Website Auth Type,basic
|
||||
Website Username,user
|
||||
Website Password,secret
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Creating Custom Crawl Logic
|
||||
|
||||
Create a BASIC dialog for custom crawling:
|
||||
|
||||
```basic
|
||||
' custom-crawl.bas
|
||||
urls = ["https://site1.com", "https://site2.com", "https://site3.com"]
|
||||
|
||||
FOR EACH url IN urls
|
||||
content = GET url
|
||||
|
||||
IF content THEN
|
||||
SAVE "crawled_pages.csv", url, content, NOW()
|
||||
SET CONTEXT content
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
TALK "Crawled " + UBOUND(urls) + " pages successfully."
|
||||
```
|
||||
|
||||
### Processing Crawled Content
|
||||
|
||||
Use LLM to process and structure crawled data:
|
||||
|
||||
```basic
|
||||
' process-crawled.bas
|
||||
pages = FIND "crawled_pages.csv"
|
||||
|
||||
FOR EACH page IN pages
|
||||
summary = LLM "Summarize this content in 3 bullet points: " + page.content
|
||||
|
||||
WITH processed
|
||||
url = page.url
|
||||
summary = summary
|
||||
processed_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "processed_content.csv", processed
|
||||
NEXT
|
||||
```
|
||||
|
||||
### Extracting Structured Data
|
||||
|
||||
Extract specific information from pages:
|
||||
|
||||
```basic
|
||||
' extract-products.bas
|
||||
SET CONTEXT "You are a data extraction assistant. Extract product information as JSON."
|
||||
|
||||
page_content = GET "https://store.example.com/products"
|
||||
|
||||
products = LLM "Extract all products with name, price, and description as JSON array: " + page_content
|
||||
|
||||
SAVE "products.json", products
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### With Knowledge Base
|
||||
|
||||
```basic
|
||||
' Add crawled content to KB
|
||||
content = GET "https://docs.example.com/api"
|
||||
|
||||
IF content THEN
|
||||
USE KB "api-docs.gbkb"
|
||||
ADD TO KB content, "API Documentation"
|
||||
END IF
|
||||
```
|
||||
|
||||
### With Notifications
|
||||
|
||||
```basic
|
||||
' Monitor for changes
|
||||
previous = GET BOT MEMORY "last_content"
|
||||
current = GET "https://news.example.com"
|
||||
|
||||
IF current <> previous THEN
|
||||
SEND EMAIL "admin@company.com", "Website Changed", "The monitored page has been updated."
|
||||
SET BOT MEMORY "last_content", current
|
||||
END IF
|
||||
```
|
||||
|
||||
### With Data Analysis
|
||||
|
||||
```basic
|
||||
' Analyze competitor pricing
|
||||
competitor_page = GET "https://competitor.com/pricing"
|
||||
|
||||
analysis = LLM "Compare this pricing to our prices and identify opportunities: " + competitor_page
|
||||
|
||||
TALK analysis
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Respect robots.txt** - Only crawl pages allowed by the site's robots.txt
|
||||
2. **Rate limiting** - Don't overwhelm target servers with requests
|
||||
3. **Set reasonable limits** - Start with low `Max Documents` values
|
||||
4. **Monitor content quality** - Review crawled content for accuracy
|
||||
5. **Keep content fresh** - Schedule periodic recrawls for dynamic sites
|
||||
6. **Handle errors gracefully** - Implement retry logic for failed requests
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| No content indexed | Invalid URL | Verify the Website URL is accessible |
|
||||
| Partial content | Max Documents too low | Increase the limit in config |
|
||||
| Stale answers | Content not refreshed | Set up scheduled recrawls |
|
||||
| Authentication errors | Missing credentials | Add auth settings to config |
|
||||
| Timeout errors | Slow target site | Increase timeout settings |
|
||||
|
||||
## Limitations
|
||||
|
||||
- JavaScript-rendered content may not be fully captured
|
||||
- Some sites block automated crawlers
|
||||
- Large sites may take significant time to fully crawl
|
||||
- Dynamic content may require special handling
|
||||
|
||||
## Related Templates
|
||||
|
||||
- `ai-search.gbai` - AI-powered document search
|
||||
- `talk-to-data.gbai` - Natural language data queries
|
||||
- `law.gbai` - Legal document processing with similar RAG approach
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Documentation Bots** - Index product docs for support
|
||||
- **Competitive Intelligence** - Monitor competitor websites
|
||||
- **News Aggregation** - Collect news from multiple sources
|
||||
- **Research Assistants** - Build knowledge bases from academic sources
|
||||
- **FAQ Generators** - Extract FAQs from help sites
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - Part of General Bots Open Source Platform.
|
||||
|
||||
---
|
||||
|
||||
**Pragmatismo** - General Bots
|
||||
208
crawler.gbai/crawler.gbkb/web-crawling-guide.md
Normal file
208
crawler.gbai/crawler.gbkb/web-crawling-guide.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# Web Crawling Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Web Crawler bot helps you extract and index content from websites. It automatically navigates through web pages, collects information, and makes it searchable through your knowledge base.
|
||||
|
||||
## Features
|
||||
|
||||
### Content Extraction
|
||||
|
||||
- **Text Content**: Extract readable text from web pages
|
||||
- **Links**: Follow and index linked pages
|
||||
- **Metadata**: Capture page titles, descriptions, and keywords
|
||||
- **Structured Data**: Extract data from tables and lists
|
||||
|
||||
### Crawl Management
|
||||
|
||||
- **Depth Control**: Set how many levels of links to follow
|
||||
- **Domain Restrictions**: Limit crawling to specific domains
|
||||
- **URL Patterns**: Include or exclude URLs by pattern
|
||||
- **Rate Limiting**: Control request frequency to avoid overloading servers
|
||||
|
||||
### Content Processing
|
||||
|
||||
- **Duplicate Detection**: Avoid indexing the same content twice
|
||||
- **Content Filtering**: Skip irrelevant pages (login, error pages, etc.)
|
||||
- **Format Conversion**: Convert HTML to clean, searchable text
|
||||
- **Language Detection**: Identify content language for proper indexing
|
||||
|
||||
## How to Use
|
||||
|
||||
### Starting a Crawl
|
||||
|
||||
To start crawling a website:
|
||||
|
||||
1. Provide the starting URL (seed URL)
|
||||
2. Configure crawl parameters (depth, limits)
|
||||
3. Start the crawl process
|
||||
4. Monitor progress and results
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `max_depth` | How many link levels to follow | 3 |
|
||||
| `max_pages` | Maximum pages to crawl | 100 |
|
||||
| `delay` | Seconds between requests | 1 |
|
||||
| `same_domain` | Stay within starting domain | true |
|
||||
| `follow_external` | Follow links to other domains | false |
|
||||
|
||||
### URL Patterns
|
||||
|
||||
You can filter URLs using patterns:
|
||||
|
||||
**Include patterns:**
|
||||
- `/blog/*` - Only crawl blog pages
|
||||
- `/products/*` - Only crawl product pages
|
||||
|
||||
**Exclude patterns:**
|
||||
- `/admin/*` - Skip admin pages
|
||||
- `/login` - Skip login pages
|
||||
- `*.pdf` - Skip PDF files
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Respectful Crawling
|
||||
|
||||
1. **Respect robots.txt**: Always check and honor robots.txt rules
|
||||
2. **Rate limiting**: Don't overload servers with too many requests
|
||||
3. **Identify yourself**: Use a proper user agent string
|
||||
4. **Off-peak hours**: Schedule large crawls during low-traffic times
|
||||
|
||||
### Efficient Crawling
|
||||
|
||||
1. **Start focused**: Begin with a specific section rather than entire site
|
||||
2. **Set limits**: Use reasonable depth and page limits
|
||||
3. **Filter content**: Exclude irrelevant sections early
|
||||
4. **Monitor progress**: Watch for errors and adjust as needed
|
||||
|
||||
### Content Quality
|
||||
|
||||
1. **Remove navigation**: Filter out repeated headers/footers
|
||||
2. **Extract main content**: Focus on the primary page content
|
||||
3. **Handle dynamic content**: Some sites require JavaScript rendering
|
||||
4. **Check encoding**: Ensure proper character encoding
|
||||
|
||||
## Common Crawl Scenarios
|
||||
|
||||
### Documentation Site
|
||||
|
||||
```
|
||||
Starting URL: https://docs.example.com/
|
||||
Depth: 4
|
||||
Include: /docs/*, /api/*
|
||||
Exclude: /changelog/*
|
||||
```
|
||||
|
||||
### Blog Archive
|
||||
|
||||
```
|
||||
Starting URL: https://blog.example.com/
|
||||
Depth: 2
|
||||
Include: /posts/*, /articles/*
|
||||
Exclude: /author/*, /tag/*
|
||||
```
|
||||
|
||||
### Product Catalog
|
||||
|
||||
```
|
||||
Starting URL: https://shop.example.com/products/
|
||||
Depth: 3
|
||||
Include: /products/*, /categories/*
|
||||
Exclude: /cart/*, /checkout/*
|
||||
```
|
||||
|
||||
## Understanding Results
|
||||
|
||||
### Crawl Statistics
|
||||
|
||||
After a crawl completes, you'll see:
|
||||
|
||||
- **Pages Crawled**: Total pages successfully processed
|
||||
- **Pages Skipped**: Pages excluded by filters
|
||||
- **Errors**: Pages that failed to load
|
||||
- **Time Elapsed**: Total crawl duration
|
||||
- **Content Size**: Total indexed content size
|
||||
|
||||
### Content Index
|
||||
|
||||
Crawled content is indexed and available for:
|
||||
|
||||
- Semantic search queries
|
||||
- Knowledge base answers
|
||||
- Document retrieval
|
||||
- AI-powered Q&A
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pages Not Crawling
|
||||
|
||||
- Check if URL is accessible (not behind login)
|
||||
- Verify robots.txt allows crawling
|
||||
- Ensure URL matches include patterns
|
||||
- Check for JavaScript-only content
|
||||
|
||||
### Slow Crawling
|
||||
|
||||
- Increase delay between requests if seeing errors
|
||||
- Reduce concurrent connections
|
||||
- Check network connectivity
|
||||
- Monitor server response times
|
||||
|
||||
### Missing Content
|
||||
|
||||
- Some sites require JavaScript rendering
|
||||
- Content may be loaded dynamically via AJAX
|
||||
- Check if content is within an iframe
|
||||
- Verify content isn't blocked by login wall
|
||||
|
||||
### Duplicate Content
|
||||
|
||||
- Enable duplicate detection
|
||||
- Use canonical URL handling
|
||||
- Filter URL parameters that don't change content
|
||||
|
||||
## Scheduled Crawling
|
||||
|
||||
Set up recurring crawls to keep content fresh:
|
||||
|
||||
- **Daily**: For frequently updated news/blog sites
|
||||
- **Weekly**: For documentation and knowledge bases
|
||||
- **Monthly**: For stable reference content
|
||||
|
||||
## Legal Considerations
|
||||
|
||||
Always ensure you have the right to crawl and index content:
|
||||
|
||||
- Check website terms of service
|
||||
- Respect copyright and intellectual property
|
||||
- Honor robots.txt directives
|
||||
- Don't crawl private or restricted content
|
||||
- Consider data protection regulations (GDPR, LGPD)
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I crawl a site that requires login?**
|
||||
A: The crawler works best with public content. For authenticated content, consider using API integrations instead.
|
||||
|
||||
**Q: Can I crawl PDF documents?**
|
||||
A: Yes, PDFs can be downloaded and processed separately for text extraction.
|
||||
|
||||
**Q: How often should I re-crawl?**
|
||||
A: Depends on how frequently the site updates. News sites may need daily crawls; documentation might only need weekly or monthly.
|
||||
|
||||
**Q: What happens if a page moves or is deleted?**
|
||||
A: The crawler will detect 404 errors and can remove outdated content from the index.
|
||||
|
||||
**Q: Can I crawl multiple sites at once?**
|
||||
A: Yes, you can configure multiple seed URLs and the crawler will process them in sequence.
|
||||
|
||||
## Support
|
||||
|
||||
For crawling issues:
|
||||
|
||||
- Review crawl logs for error details
|
||||
- Check network and firewall settings
|
||||
- Verify target site is accessible
|
||||
- Contact your administrator for configuration help
|
||||
6
crawler.gbai/crawler.gbot/config.csv
Normal file
6
crawler.gbai/crawler.gbot/config.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,value
|
||||
Website,https://pragmatismo.com.br/
|
||||
website Max Documents, 2
|
||||
Answer Mode,document
|
||||
Theme Color,purple
|
||||
LLM Provider,openai
|
||||
|
307
crm/contacts.gbai/README.md
Normal file
307
crm/contacts.gbai/README.md
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
# CRM Contacts Template (contacts.gbai)
|
||||
|
||||
A General Bots template for managing contact directories with search, add, update, and company management capabilities.
|
||||
|
||||
## Overview
|
||||
|
||||
The Contacts template provides a complete contact management system with natural language interaction. Users can add new contacts, search the directory, manage company records, and track contact history through conversational AI.
|
||||
|
||||
## Features
|
||||
|
||||
- **Contact Management** - Add, update, search, and delete contacts
|
||||
- **Company Records** - Automatic company creation and association
|
||||
- **Flexible Search** - Search by name, email, company, or phone
|
||||
- **Activity Tracking** - Log all contact interactions
|
||||
- **Tag System** - Organize contacts with custom tags
|
||||
- **Export Capabilities** - Export contact lists in various formats
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
contacts.gbai/
|
||||
├── README.md
|
||||
├── contacts.gbdialog/
|
||||
│ ├── start.bas # Main entry point and tool registration
|
||||
│ ├── add-contact.bas # Add new contacts
|
||||
│ └── search-contact.bas # Search contact directory
|
||||
├── contacts.gbkb/ # Knowledge base for contact help
|
||||
└── contacts.gbot/
|
||||
└── config.csv # Bot configuration
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `start.bas` | Initializes tools, sets context, and displays welcome menu |
|
||||
| `add-contact.bas` | Creates new contact records with validation |
|
||||
| `search-contact.bas` | Searches directory by multiple fields |
|
||||
|
||||
## Available Tools
|
||||
|
||||
The template registers these tools for LLM access:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `add-contact` | Add a new contact to the directory |
|
||||
| `search-contact` | Search contacts by any field |
|
||||
| `update-contact` | Modify existing contact information |
|
||||
| `list-contacts` | List all contacts with optional filters |
|
||||
| `add-company` | Create a new company record |
|
||||
| `contact-history` | View interaction history for a contact |
|
||||
|
||||
## Data Schema
|
||||
|
||||
### Contacts Table
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `contactid` | String | Unique identifier (CON-YYYYMMDD-XXXX) |
|
||||
| `firstname` | String | Contact's first name |
|
||||
| `lastname` | String | Contact's last name |
|
||||
| `fullname` | String | Combined full name |
|
||||
| `email` | Email | Email address |
|
||||
| `phone` | Phone | Phone number |
|
||||
| `companyname` | String | Associated company |
|
||||
| `jobtitle` | String | Job title or role |
|
||||
| `tags` | String | Comma-separated tags |
|
||||
| `notes` | String | Additional notes |
|
||||
| `createdby` | String | User who created the record |
|
||||
| `createdat` | DateTime | Creation timestamp |
|
||||
|
||||
### Companies Table
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `companyid` | String | Unique identifier |
|
||||
| `name` | String | Company name |
|
||||
| `createdat` | DateTime | Creation timestamp |
|
||||
|
||||
### Activities Table
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `contactid` | String | Related contact ID |
|
||||
| `action` | String | Action description |
|
||||
| `createdby` | String | User who performed action |
|
||||
| `createdat` | DateTime | Activity timestamp |
|
||||
|
||||
## Usage
|
||||
|
||||
### Adding a Contact
|
||||
|
||||
Users can add contacts naturally:
|
||||
|
||||
- "Add John Smith from Acme Corp"
|
||||
- "Create a new contact for jane@company.com"
|
||||
- "Add contact: Mike Johnson, Sales Manager at TechCo"
|
||||
|
||||
Or provide structured input:
|
||||
|
||||
```
|
||||
First Name: John
|
||||
Last Name: Smith
|
||||
Email: john.smith@acme.com
|
||||
Phone: +1-555-123-4567
|
||||
Company: Acme Corporation
|
||||
Job Title: VP of Sales
|
||||
Tags: customer, vip
|
||||
Notes: Met at trade show
|
||||
```
|
||||
|
||||
### Searching Contacts
|
||||
|
||||
Search using natural language:
|
||||
|
||||
- "Find contacts at Acme"
|
||||
- "Search for John"
|
||||
- "Look up john.smith@acme.com"
|
||||
- "Find all VIP contacts"
|
||||
|
||||
Search filters:
|
||||
|
||||
| Filter | Example |
|
||||
|--------|---------|
|
||||
| By name | "search John Smith" |
|
||||
| By email | "search john@company.com" |
|
||||
| By company | "find contacts at Microsoft" |
|
||||
| By phone | "lookup +1-555-1234" |
|
||||
| By tag | "show all VIP contacts" |
|
||||
|
||||
### Managing Companies
|
||||
|
||||
Companies are auto-created when adding contacts:
|
||||
|
||||
```basic
|
||||
' When adding a contact with a new company
|
||||
IF companyname THEN
|
||||
existingcompany = FIND "companies.csv", "name=" + companyname
|
||||
IF COUNT(existingcompany) = 0 THEN
|
||||
' Auto-create company record
|
||||
SAVE "companies.csv", companyid, companyname, createdat
|
||||
END IF
|
||||
END IF
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure in `contacts.gbot/config.csv`:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `Theme Color` | UI accent color | `blue` |
|
||||
| `Default Tags` | Auto-applied tags | `new,prospect` |
|
||||
| `Require Email` | Email required? | `true` |
|
||||
| `Duplicate Check` | Check for duplicates | `true` |
|
||||
|
||||
## Customization
|
||||
|
||||
### Adding Custom Fields
|
||||
|
||||
Extend the contact schema in `add-contact.bas`:
|
||||
|
||||
```basic
|
||||
PARAM department AS STRING LIKE "Engineering" DESCRIPTION "Department name" OPTIONAL
|
||||
PARAM linkedin AS STRING LIKE "linkedin.com/in/john" DESCRIPTION "LinkedIn profile" OPTIONAL
|
||||
|
||||
' Include in save
|
||||
SAVE "contacts.csv", contactid, firstname, lastname, fullname, email, phone,
|
||||
companyname, jobtitle, department, linkedin, tags, notes, createdby, createdat
|
||||
```
|
||||
|
||||
### Custom Search Filters
|
||||
|
||||
Add specialized search in `search-contact.bas`:
|
||||
|
||||
```basic
|
||||
IF searchby = "department" THEN
|
||||
results = FIND "contacts.csv", "department LIKE " + searchterm
|
||||
END IF
|
||||
|
||||
IF searchby = "recent" THEN
|
||||
cutoff = DATEADD(NOW(), -7, "day")
|
||||
results = FIND "contacts.csv", "createdat >= " + cutoff
|
||||
END IF
|
||||
```
|
||||
|
||||
### Integration with Email
|
||||
|
||||
Send welcome emails to new contacts:
|
||||
|
||||
```basic
|
||||
' In add-contact.bas after SAVE
|
||||
IF email THEN
|
||||
subject = "Thank you for connecting!"
|
||||
body = "Hi " + firstname + ", we look forward to working with you."
|
||||
SEND EMAIL email, subject, body
|
||||
END IF
|
||||
```
|
||||
|
||||
### Integration with CRM Pipeline
|
||||
|
||||
Connect contacts to leads:
|
||||
|
||||
```basic
|
||||
' Convert contact to lead
|
||||
PARAM convert_to_lead AS BOOLEAN DESCRIPTION "Create lead from contact" OPTIONAL
|
||||
|
||||
IF convert_to_lead THEN
|
||||
WITH new_lead
|
||||
id = FORMAT(GUID())
|
||||
name = fullname
|
||||
email = email
|
||||
phone = phone
|
||||
company = companyname
|
||||
source = "contact_conversion"
|
||||
status = "new"
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "leads.csv", new_lead
|
||||
TALK "Lead created from contact: " + fullname
|
||||
END IF
|
||||
```
|
||||
|
||||
## Example Conversations
|
||||
|
||||
### Adding a Contact
|
||||
```
|
||||
User: Add a new contact
|
||||
Bot: I'll help you add a new contact. What is their first name?
|
||||
User: Sarah
|
||||
Bot: And their last name?
|
||||
User: Johnson
|
||||
Bot: What is Sarah's email address?
|
||||
User: sarah.johnson@techstartup.io
|
||||
Bot: Phone number? (optional)
|
||||
User: 555-987-6543
|
||||
Bot: What company does Sarah work for?
|
||||
User: TechStartup Inc
|
||||
Bot: What is her job title?
|
||||
User: CTO
|
||||
Bot: ✅ Contact added: Sarah Johnson
|
||||
ID: CON-20240115-4821
|
||||
Email: sarah.johnson@techstartup.io
|
||||
Phone: 555-987-6543
|
||||
Company: TechStartup Inc
|
||||
Title: CTO
|
||||
```
|
||||
|
||||
### Searching Contacts
|
||||
```
|
||||
User: Find contacts at TechStartup
|
||||
Bot: Found 2 contact(s):
|
||||
---
|
||||
**Sarah Johnson**
|
||||
sarah.johnson@techstartup.io
|
||||
555-987-6543
|
||||
TechStartup Inc
|
||||
CTO
|
||||
ID: CON-20240115-4821
|
||||
---
|
||||
**Mike Chen**
|
||||
mike.chen@techstartup.io
|
||||
TechStartup Inc
|
||||
Developer
|
||||
ID: CON-20240110-3392
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Validate input** - Use `PARAM` type validation for email and phone
|
||||
2. **Check duplicates** - Search before creating to avoid duplicates
|
||||
3. **Log activities** - Track all changes for audit trail
|
||||
4. **Use tags** - Organize contacts with meaningful tags
|
||||
5. **Keep notes** - Add context about how/where you met contacts
|
||||
6. **Regular cleanup** - Archive inactive contacts periodically
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Duplicate contacts | Enable duplicate checking in config |
|
||||
| Search returns nothing | Try broader search terms |
|
||||
| Company not linked | Ensure company name matches exactly |
|
||||
| Missing activities | Check activity logging is enabled |
|
||||
|
||||
## Related Templates
|
||||
|
||||
- `crm.gbai` - Full CRM with leads, opportunities, and pipeline
|
||||
- `marketing.gbai` - Marketing automation with contact segmentation
|
||||
- `office.gbai` - Office productivity with contact directory
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Sales Teams** - Manage prospect and customer contacts
|
||||
- **HR Departments** - Employee and candidate directories
|
||||
- **Event Management** - Attendee and speaker contacts
|
||||
- **Networking** - Professional contact management
|
||||
- **Customer Support** - Customer contact lookup
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - Part of General Bots Open Source Platform.
|
||||
|
||||
---
|
||||
|
||||
**Pragmatismo** - General Bots
|
||||
56
crm/contacts.gbai/contacts.gbdialog/add-contact.bas
Normal file
56
crm/contacts.gbai/contacts.gbdialog/add-contact.bas
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
PARAM firstname AS STRING LIKE "John" DESCRIPTION "First name of the contact"
|
||||
PARAM lastname AS STRING LIKE "Smith" DESCRIPTION "Last name of the contact"
|
||||
PARAM email AS EMAIL LIKE "john.smith@company.com" DESCRIPTION "Email address"
|
||||
PARAM phone AS PHONE LIKE "+1-555-123-4567" DESCRIPTION "Phone number"
|
||||
PARAM companyname AS STRING LIKE "Acme Corporation" DESCRIPTION "Company or organization"
|
||||
PARAM jobtitle AS STRING LIKE "Sales Manager" DESCRIPTION "Job title or role"
|
||||
PARAM tags AS STRING LIKE "customer,vip" DESCRIPTION "Comma-separated tags" OPTIONAL
|
||||
PARAM notes AS STRING LIKE "Met at conference" DESCRIPTION "Notes about the contact" OPTIONAL
|
||||
|
||||
DESCRIPTION "Add a new contact to the directory with contact information"
|
||||
|
||||
contactid = "CON-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
createdat = FORMAT(NOW(), "YYYY-MM-DD HH:mm:ss")
|
||||
createdby = GET "session.user_email"
|
||||
fullname = firstname + " " + lastname
|
||||
|
||||
SAVE "contacts.csv", contactid, firstname, lastname, fullname, email, phone, companyname, jobtitle, tags, notes, createdby, createdat
|
||||
|
||||
SET BOT MEMORY "last_contact", contactid
|
||||
|
||||
IF companyname THEN
|
||||
existingcompany = FIND "companies.csv", "name=" + companyname
|
||||
companycount = AGGREGATE "COUNT", existingcompany, "id"
|
||||
|
||||
IF companycount = 0 THEN
|
||||
companyid = "COMP-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
SAVE "companies.csv", companyid, companyname, createdat
|
||||
END IF
|
||||
END IF
|
||||
|
||||
WITH activity
|
||||
contactid = contactid
|
||||
action = "Contact created: " + fullname
|
||||
createdby = createdby
|
||||
createdat = createdat
|
||||
END WITH
|
||||
|
||||
SAVE "contact_activities.csv", activity
|
||||
|
||||
TALK "Contact added: " + fullname
|
||||
TALK "ID: " + contactid
|
||||
TALK "Email: " + email
|
||||
|
||||
IF phone THEN
|
||||
TALK "Phone: " + phone
|
||||
END IF
|
||||
|
||||
IF companyname THEN
|
||||
TALK "Company: " + companyname
|
||||
END IF
|
||||
|
||||
IF jobtitle THEN
|
||||
TALK "Title: " + jobtitle
|
||||
END IF
|
||||
|
||||
RETURN contactid
|
||||
69
crm/contacts.gbai/contacts.gbdialog/search-contact.bas
Normal file
69
crm/contacts.gbai/contacts.gbdialog/search-contact.bas
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
PARAM searchterm AS STRING LIKE "john" DESCRIPTION "Name, email, company, or phone to search for"
|
||||
PARAM searchby AS STRING LIKE "all" DESCRIPTION "Filter by field: all, name, email, company, phone"
|
||||
|
||||
DESCRIPTION "Search contact directory by name, email, company, or phone number"
|
||||
|
||||
IF NOT searchby THEN
|
||||
searchby = "all"
|
||||
END IF
|
||||
|
||||
TALK "Searching contacts for: " + searchterm
|
||||
|
||||
results = []
|
||||
|
||||
IF searchby = "all" OR searchby = "name" THEN
|
||||
nameresults = FIND "contacts.csv", "fullname LIKE " + searchterm
|
||||
results = MERGE results, nameresults
|
||||
END IF
|
||||
|
||||
IF searchby = "all" OR searchby = "email" THEN
|
||||
emailresults = FIND "contacts.csv", "email LIKE " + searchterm
|
||||
results = MERGE results, emailresults
|
||||
END IF
|
||||
|
||||
IF searchby = "all" OR searchby = "company" THEN
|
||||
companyresults = FIND "contacts.csv", "companyname LIKE " + searchterm
|
||||
results = MERGE results, companyresults
|
||||
END IF
|
||||
|
||||
IF searchby = "all" OR searchby = "phone" THEN
|
||||
phoneresults = FIND "contacts.csv", "phone LIKE " + searchterm
|
||||
results = MERGE results, phoneresults
|
||||
END IF
|
||||
|
||||
resultcount = UBOUND(results)
|
||||
|
||||
IF resultcount = 0 THEN
|
||||
TALK "No contacts found matching: " + searchterm
|
||||
RETURN
|
||||
END IF
|
||||
|
||||
TALK "Found " + resultcount + " contact(s):"
|
||||
|
||||
FOR EACH contact IN results
|
||||
TALK "---"
|
||||
TALK "**" + contact.fullname + "**"
|
||||
TALK contact.email
|
||||
|
||||
IF contact.phone <> "" THEN
|
||||
TALK contact.phone
|
||||
END IF
|
||||
|
||||
IF contact.companyname <> "" THEN
|
||||
TALK contact.companyname
|
||||
END IF
|
||||
|
||||
IF contact.jobtitle <> "" THEN
|
||||
TALK contact.jobtitle
|
||||
END IF
|
||||
|
||||
TALK "ID: " + contact.contactid
|
||||
NEXT
|
||||
|
||||
IF resultcount > 0 THEN
|
||||
firstcontact = FIRST results
|
||||
SET BOT MEMORY "last_contact", firstcontact.contactid
|
||||
SET BOT MEMORY "last_search", searchterm
|
||||
END IF
|
||||
|
||||
RETURN results
|
||||
42
crm/contacts.gbai/contacts.gbdialog/start.bas
Normal file
42
crm/contacts.gbai/contacts.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
ADD TOOL "add-contact"
|
||||
ADD TOOL "search-contact"
|
||||
ADD TOOL "update-contact"
|
||||
ADD TOOL "list-contacts"
|
||||
ADD TOOL "add-company"
|
||||
ADD TOOL "contact-history"
|
||||
|
||||
USE KB "contacts.gbkb"
|
||||
|
||||
SET CONTEXT "contact directory" AS "You are a contact management assistant helping organize and search contacts. Help with adding new contacts, searching the directory, updating contact information, managing company records, and viewing contact history."
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "add" AS "Add a new contact"
|
||||
ADD SUGGESTION "search" AS "Search contacts"
|
||||
ADD SUGGESTION "companies" AS "View companies"
|
||||
ADD SUGGESTION "recent" AS "Recent contacts"
|
||||
ADD SUGGESTION "export" AS "Export contacts"
|
||||
|
||||
BEGIN TALK
|
||||
**Contact Directory**
|
||||
|
||||
I can help you with:
|
||||
• Add new contacts and companies
|
||||
• Search by name, email, or company
|
||||
• Update contact information
|
||||
• Manage company records
|
||||
• View contact history
|
||||
• Export contact lists
|
||||
|
||||
Select an option or tell me what you need.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a contact directory assistant.
|
||||
|
||||
Contact fields: name, email, phone, company, job title, address, tags, notes.
|
||||
|
||||
Confirm before making changes to existing contacts.
|
||||
Be flexible with partial matches when searching.
|
||||
Suggest adding missing information when appropriate.
|
||||
END SYSTEM PROMPT
|
||||
9
crm/contacts.gbai/contacts.gbot/config.csv
Normal file
9
crm/contacts.gbai/contacts.gbot/config.csv
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name,value
|
||||
episodic-memory-history,2
|
||||
episodic-memory-threshold,4
|
||||
theme-color1,#1565C0
|
||||
theme-color2,#E3F2FD
|
||||
theme-logo,https://pragmatismo.com.br/icons/general-bots.svg
|
||||
theme-title,Contact Directory - General Bots
|
||||
bot-name,Contact Directory Assistant
|
||||
welcome-message,Welcome to the Contact Directory!
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
PARAM dealname AS STRING LIKE "Acme Corp Enterprise License" DESCRIPTION "Name of the deal or opportunity"
|
||||
PARAM companyname AS STRING LIKE "Acme Corporation" DESCRIPTION "Company or account name"
|
||||
PARAM contactemail AS EMAIL LIKE "john@acme.com" DESCRIPTION "Primary contact email"
|
||||
PARAM dealvalue AS MONEY LIKE 50000 DESCRIPTION "Estimated deal value in dollars"
|
||||
PARAM stage AS STRING LIKE "Lead" DESCRIPTION "Initial stage: Lead, Qualified, Proposal, Negotiation" OPTIONAL
|
||||
PARAM closedate AS DATE LIKE "2025-03-30" DESCRIPTION "Expected close date" OPTIONAL
|
||||
PARAM notes AS STRING LIKE "Met at trade show" DESCRIPTION "Notes about the deal" OPTIONAL
|
||||
|
||||
DESCRIPTION "Create a new sales deal in the pipeline with deal information and value tracking"
|
||||
|
||||
IF NOT stage THEN
|
||||
stage = "Lead"
|
||||
END IF
|
||||
|
||||
IF NOT closedate THEN
|
||||
closedate = DATEADD(TODAY(), 30, "day")
|
||||
END IF
|
||||
|
||||
dealid = "DEAL-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
createdat = FORMAT(NOW(), "YYYY-MM-DD HH:mm:ss")
|
||||
ownerid = GET "session.user_id"
|
||||
owneremail = GET "session.user_email"
|
||||
|
||||
probability = 10
|
||||
IF stage = "Qualified" THEN
|
||||
probability = 25
|
||||
ELSE IF stage = "Proposal" THEN
|
||||
probability = 50
|
||||
ELSE IF stage = "Negotiation" THEN
|
||||
probability = 75
|
||||
END IF
|
||||
|
||||
weightedvalue = dealvalue * probability / 100
|
||||
|
||||
WITH deal
|
||||
id = dealid
|
||||
name = dealname
|
||||
company = companyname
|
||||
contact = contactemail
|
||||
value = dealvalue
|
||||
currentStage = stage
|
||||
expectedClose = closedate
|
||||
prob = probability
|
||||
weighted = weightedvalue
|
||||
dealNotes = notes
|
||||
owner = ownerid
|
||||
ownerEmail = owneremail
|
||||
created = createdat
|
||||
END WITH
|
||||
|
||||
SAVE "deals.csv", deal
|
||||
|
||||
SET BOT MEMORY "last_deal", dealid
|
||||
|
||||
WITH dealActivity
|
||||
dealId = dealid
|
||||
action = "Deal created: " + dealname
|
||||
user = owneremail
|
||||
timestamp = createdat
|
||||
END WITH
|
||||
|
||||
SAVE "deal_activities.csv", dealActivity
|
||||
|
||||
TALK "Deal created: " + dealname
|
||||
TALK "ID: " + dealid
|
||||
TALK "Company: " + companyname
|
||||
TALK "Value: $" + FORMAT(dealvalue, "#,##0")
|
||||
TALK "Stage: " + stage + " (" + probability + "% probability)"
|
||||
TALK "Expected Close: " + closedate
|
||||
TALK "Weighted Value: $" + FORMAT(weightedvalue, "#,##0")
|
||||
|
||||
RETURN dealid
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
PARAM jobname AS STRING DESCRIPTION "Name of the scheduled job to execute"
|
||||
|
||||
DESCRIPTION "Scheduled jobs for pipeline maintenance, reminders, and reporting. Run automatically based on configured schedules."
|
||||
|
||||
' ============================================================================
|
||||
' DAILY PIPELINE REPORT - Runs at 8:00 AM every day
|
||||
' ============================================================================
|
||||
|
||||
IF jobname = "daily report" THEN
|
||||
SET SCHEDULE "0 8 * * *"
|
||||
|
||||
' Get pipeline summary
|
||||
let deals = FIND "deals.csv"
|
||||
let opendeals = FILTER deals, "stage!=Closed Won AND stage!=Closed Lost"
|
||||
|
||||
' Calculate metrics
|
||||
let totalvalue = AGGREGATE "SUM", opendeals, "dealvalue"
|
||||
let weightedvalue = AGGREGATE "SUM", opendeals, "weightedvalue"
|
||||
let dealcount = AGGREGATE "COUNT", opendeals, "dealid"
|
||||
|
||||
' Count by stage
|
||||
let leads = FILTER opendeals, "stage=Lead"
|
||||
let qualified = FILTER opendeals, "stage=Qualified"
|
||||
let proposals = FILTER opendeals, "stage=Proposal"
|
||||
let negotiations = FILTER opendeals, "stage=Negotiation"
|
||||
|
||||
let leadcount = AGGREGATE "COUNT", leads, "dealid"
|
||||
let qualifiedcount = AGGREGATE "COUNT", qualified, "dealid"
|
||||
let proposalcount = AGGREGATE "COUNT", proposals, "dealid"
|
||||
let negotiationcount = AGGREGATE "COUNT", negotiations, "dealid"
|
||||
|
||||
' Build report
|
||||
let reportdate = FORMAT TODAY() AS "MMMM DD, YYYY"
|
||||
let subject = "Daily Pipeline Report - " + reportdate
|
||||
|
||||
let message = "Good morning!\n\n"
|
||||
message = message + "Here is your daily pipeline summary:\n\n"
|
||||
message = message + "PIPELINE OVERVIEW\n"
|
||||
message = message + "================\n"
|
||||
message = message + "Total Open Deals: " + dealcount + "\n"
|
||||
message = message + "Total Pipeline Value: $" + FORMAT totalvalue AS "#,##0" + "\n"
|
||||
message = message + "Weighted Value: $" + FORMAT weightedvalue AS "#,##0" + "\n\n"
|
||||
message = message + "BY STAGE\n"
|
||||
message = message + "========\n"
|
||||
message = message + "Lead: " + leadcount + " deals\n"
|
||||
message = message + "Qualified: " + qualifiedcount + " deals\n"
|
||||
message = message + "Proposal: " + proposalcount + " deals\n"
|
||||
message = message + "Negotiation: " + negotiationcount + " deals\n\n"
|
||||
message = message + "Have a great selling day!\n"
|
||||
message = message + "- Sales Pipeline Bot"
|
||||
|
||||
SEND MAIL "sales-team@company.com", subject, message
|
||||
|
||||
TALK "Daily pipeline report sent"
|
||||
END IF
|
||||
|
||||
' ============================================================================
|
||||
' STALE DEAL ALERTS - Runs at 9:00 AM every day
|
||||
' ============================================================================
|
||||
|
||||
IF jobname = "stale alerts" THEN
|
||||
SET SCHEDULE "0 9 * * *"
|
||||
|
||||
' Find deals not updated in 7+ days
|
||||
let cutoffdate = FORMAT DATEADD(TODAY(), -7, "day") AS "YYYY-MM-DD"
|
||||
let deals = FIND "deals.csv"
|
||||
let staledeals = FILTER deals, "updatedat<" + cutoffdate + " AND stage!=Closed Won AND stage!=Closed Lost"
|
||||
|
||||
let stalecount = AGGREGATE "COUNT", staledeals, "dealid"
|
||||
|
||||
IF stalecount > 0 THEN
|
||||
FOR EACH deal IN staledeals
|
||||
let stalealert = "Your deal '" + deal.dealname + "' has not been updated in over 7 days.\n"
|
||||
stalealert = stalealert + "Deal ID: " + deal.dealid + "\n"
|
||||
stalealert = stalealert + "Current Stage: " + deal.stage + "\n"
|
||||
stalealert = stalealert + "Value: $" + FORMAT deal.dealvalue AS "#,##0" + "\n\n"
|
||||
stalealert = stalealert + "Please review and update this deal or mark it as closed."
|
||||
|
||||
SEND MAIL deal.owneremail, "Action Required: Stale Deal Alert", stalealert
|
||||
NEXT deal
|
||||
|
||||
TALK "Sent " + stalecount + " stale deal alerts"
|
||||
ELSE
|
||||
TALK "No stale deals found"
|
||||
END IF
|
||||
END IF
|
||||
|
||||
' ============================================================================
|
||||
' CLOSE DATE REMINDERS - Runs at 8:30 AM every day
|
||||
' ============================================================================
|
||||
|
||||
IF jobname = "close reminders" THEN
|
||||
SET SCHEDULE "30 8 * * *"
|
||||
|
||||
' Find deals closing in next 7 days
|
||||
let today = FORMAT TODAY() AS "YYYY-MM-DD"
|
||||
let nextweek = FORMAT DATEADD(TODAY(), 7, "day") AS "YYYY-MM-DD"
|
||||
let deals = FIND "deals.csv"
|
||||
let upcoming = FILTER deals, "closedate>=" + today + " AND closedate<=" + nextweek + " AND stage!=Closed Won AND stage!=Closed Lost"
|
||||
|
||||
let upcomingcount = AGGREGATE "COUNT", upcoming, "dealid"
|
||||
|
||||
IF upcomingcount > 0 THEN
|
||||
FOR EACH deal IN upcoming
|
||||
let daysuntil = DATEDIFF(TODAY(), deal.closedate, "day")
|
||||
let reminder = "Deal Closing Soon!\n\n"
|
||||
reminder = reminder + "Your deal '" + deal.dealname + "' is expected to close in " + daysuntil + " days.\n\n"
|
||||
reminder = reminder + "Deal Details:\n"
|
||||
reminder = reminder + "- Company: " + deal.companyname + "\n"
|
||||
reminder = reminder + "- Value: $" + FORMAT deal.dealvalue AS "#,##0" + "\n"
|
||||
reminder = reminder + "- Stage: " + deal.stage + "\n"
|
||||
reminder = reminder + "- Close Date: " + deal.closedate + "\n\n"
|
||||
reminder = reminder + "Make sure to follow up and push for the close!"
|
||||
|
||||
SEND MAIL deal.owneremail, "Reminder: Deal Closing in " + daysuntil + " Days", reminder
|
||||
NEXT deal
|
||||
|
||||
TALK "Sent " + upcomingcount + " close date reminders"
|
||||
ELSE
|
||||
TALK "No deals closing in the next 7 days"
|
||||
END IF
|
||||
END IF
|
||||
|
||||
' ============================================================================
|
||||
' WEEKLY FORECAST REPORT - Runs at 9:00 AM every Monday
|
||||
' ============================================================================
|
||||
|
||||
IF jobname = "weekly forecast" THEN
|
||||
SET SCHEDULE "0 9 * * 1"
|
||||
|
||||
' Get this month's deals
|
||||
let monthstart = FORMAT DATEADD(TODAY(), 0, "month") AS "YYYY-MM-01"
|
||||
let monthend = FORMAT DATEADD(TODAY(), 1, "month") AS "YYYY-MM-01"
|
||||
let deals = FIND "deals.csv"
|
||||
let monthdeals = FILTER deals, "closedate>=" + monthstart + " AND closedate<" + monthend
|
||||
|
||||
' Calculate forecast
|
||||
let totalforecast = AGGREGATE "SUM", monthdeals, "weightedvalue"
|
||||
let wondeals = FILTER monthdeals, "stage=Closed Won"
|
||||
let closedvalue = AGGREGATE "SUM", wondeals, "dealvalue"
|
||||
|
||||
' Build forecast report
|
||||
let reportdate = FORMAT TODAY() AS "MMMM DD, YYYY"
|
||||
let monthname = FORMAT TODAY() AS "MMMM YYYY"
|
||||
let subject = "Weekly Sales Forecast - " + reportdate
|
||||
|
||||
let message = "Weekly Forecast Report\n"
|
||||
message = message + "======================\n\n"
|
||||
message = message + "Month: " + monthname + "\n\n"
|
||||
message = message + "Forecast Summary:\n"
|
||||
message = message + "- Weighted Forecast: $" + FORMAT totalforecast AS "#,##0" + "\n"
|
||||
message = message + "- Already Closed: $" + FORMAT closedvalue AS "#,##0" + "\n"
|
||||
message = message + "- Remaining to Close: $" + FORMAT (totalforecast - closedvalue) AS "#,##0" + "\n\n"
|
||||
message = message + "Keep pushing to hit your targets!\n"
|
||||
message = message + "- Sales Pipeline Bot"
|
||||
|
||||
SEND MAIL "sales-team@company.com", subject, message
|
||||
|
||||
TALK "Weekly forecast report sent"
|
||||
END IF
|
||||
|
||||
' ============================================================================
|
||||
' SETUP SCHEDULES - Run once to configure all jobs
|
||||
' ============================================================================
|
||||
|
||||
IF jobname = "setup schedules" THEN
|
||||
TALK "Configuring pipeline scheduled jobs..."
|
||||
TALK ""
|
||||
TALK "📅 **Scheduled Jobs:**"
|
||||
TALK "• Daily Pipeline Report: 8:00 AM daily"
|
||||
TALK "• Stale Deal Alerts: 9:00 AM daily"
|
||||
TALK "• Close Date Reminders: 8:30 AM daily"
|
||||
TALK "• Weekly Forecast: 9:00 AM Mondays"
|
||||
TALK ""
|
||||
TALK "✅ All pipeline schedules configured!"
|
||||
END IF
|
||||
48
crm/sales-pipeline.gbai/sales-pipeline.gbdialog/start.bas
Normal file
48
crm/sales-pipeline.gbai/sales-pipeline.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
ADD TOOL "create-deal"
|
||||
ADD TOOL "update-stage"
|
||||
ADD TOOL "list-deals"
|
||||
ADD TOOL "deal-details"
|
||||
ADD TOOL "pipeline-report"
|
||||
ADD TOOL "forecast-revenue"
|
||||
|
||||
USE KB "sales-pipeline.gbkb"
|
||||
|
||||
SET CONTEXT "sales pipeline" AS "You are a sales assistant helping manage the sales pipeline. Help with creating new deals, updating deal stages, viewing pipeline status, generating sales forecasts, and analyzing win/loss rates."
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "newdeal" AS "Create a new deal"
|
||||
ADD SUGGESTION "pipeline" AS "Show my pipeline"
|
||||
ADD SUGGESTION "update" AS "Update a deal stage"
|
||||
ADD SUGGESTION "forecast" AS "View sales forecast"
|
||||
ADD SUGGESTION "report" AS "Generate pipeline report"
|
||||
|
||||
BEGIN TALK
|
||||
**Sales Pipeline Manager**
|
||||
|
||||
I can help you with:
|
||||
• Create new deals and opportunities
|
||||
• View and manage your pipeline
|
||||
• Update deal stages
|
||||
• Generate sales forecasts
|
||||
• Pipeline analytics and reports
|
||||
• Track win/loss rates
|
||||
|
||||
Select an option or tell me what you need.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are a sales pipeline assistant.
|
||||
|
||||
Pipeline stages:
|
||||
- Lead: Initial contact, not qualified
|
||||
- Qualified: Budget, authority, need, timeline confirmed
|
||||
- Proposal: Quote sent
|
||||
- Negotiation: Active discussions
|
||||
- Closed Won: Successfully closed
|
||||
- Closed Lost: Lost or no decision
|
||||
|
||||
Always encourage sales reps and provide actionable insights.
|
||||
Confirm changes before saving.
|
||||
Use currency format for amounts.
|
||||
END SYSTEM PROMPT
|
||||
130
crm/sales-pipeline.gbai/sales-pipeline.gbdialog/update-stage.bas
Normal file
130
crm/sales-pipeline.gbai/sales-pipeline.gbdialog/update-stage.bas
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
PARAM dealid AS STRING LIKE "DEAL-20250115-1234" DESCRIPTION "The deal ID to update"
|
||||
PARAM newstage AS STRING LIKE "Qualified" DESCRIPTION "New stage: Lead, Qualified, Proposal, Negotiation, Closed Won, Closed Lost"
|
||||
PARAM reason AS STRING LIKE "Budget confirmed" DESCRIPTION "Optional reason for stage change"
|
||||
|
||||
DESCRIPTION "Updates the stage of an existing deal in the pipeline. Automatically updates probability and logs the activity."
|
||||
|
||||
' Validate deal ID
|
||||
IF dealid = "" THEN
|
||||
TALK "Which deal would you like to update? Please provide the deal ID."
|
||||
HEAR dealid AS STRING
|
||||
END IF
|
||||
|
||||
IF dealid = "" THEN
|
||||
let lastdeal = GET BOT MEMORY("last_deal")
|
||||
IF lastdeal != "" THEN
|
||||
TALK "Would you like to update the last deal: " + lastdeal + "?"
|
||||
HEAR confirm AS BOOLEAN
|
||||
IF confirm THEN
|
||||
dealid = lastdeal
|
||||
ELSE
|
||||
TALK "Please provide a deal ID to update."
|
||||
RETURN
|
||||
END IF
|
||||
ELSE
|
||||
TALK "I need a deal ID to update."
|
||||
RETURN
|
||||
END IF
|
||||
END IF
|
||||
|
||||
' Validate new stage
|
||||
IF newstage = "" THEN
|
||||
TALK "What stage should this deal move to?"
|
||||
TALK "Options: Lead, Qualified, Proposal, Negotiation, Closed Won, Closed Lost"
|
||||
HEAR newstage AS STRING
|
||||
END IF
|
||||
|
||||
' Normalize stage name
|
||||
newstage = UCASE(LEFT(newstage, 1)) + LCASE(MID(newstage, 2))
|
||||
|
||||
' Validate stage is valid
|
||||
let validstages = "Lead,Qualified,Proposal,Negotiation,Closed Won,Closed Lost"
|
||||
IF INSTR(validstages, newstage) = 0 THEN
|
||||
TALK "Invalid stage. Please use: Lead, Qualified, Proposal, Negotiation, Closed Won, or Closed Lost"
|
||||
RETURN
|
||||
END IF
|
||||
|
||||
' Determine probability based on stage
|
||||
let probability = 10
|
||||
|
||||
IF newstage = "Lead" THEN
|
||||
probability = 10
|
||||
ELSE IF newstage = "Qualified" THEN
|
||||
probability = 25
|
||||
ELSE IF newstage = "Proposal" THEN
|
||||
probability = 50
|
||||
ELSE IF newstage = "Negotiation" THEN
|
||||
probability = 75
|
||||
ELSE IF newstage = "Closed Won" THEN
|
||||
probability = 100
|
||||
ELSE IF newstage = "Closed Lost" THEN
|
||||
probability = 0
|
||||
END IF
|
||||
|
||||
' Get current timestamp
|
||||
let updatedat = FORMAT NOW() AS "YYYY-MM-DD HH:mm:ss"
|
||||
let useremail = GET "session.user_email"
|
||||
|
||||
' Update the deal
|
||||
UPDATE "deals.csv", "dealid=" + dealid, stage, probability, updatedat
|
||||
|
||||
' Log activity
|
||||
let activity = "Stage changed to " + newstage
|
||||
IF reason != "" THEN
|
||||
activity = activity + " - " + reason
|
||||
END IF
|
||||
|
||||
SAVE "deal_activities.csv", dealid, activity, useremail, updatedat
|
||||
|
||||
' Store last updated deal
|
||||
SET BOT MEMORY "last_deal", dealid
|
||||
|
||||
' Respond based on stage
|
||||
IF newstage = "Closed Won" THEN
|
||||
TALK "🎉 **Congratulations! Deal Won!**"
|
||||
TALK ""
|
||||
TALK "**Deal " + dealid + "** has been marked as **Closed Won**!"
|
||||
TALK "Great work closing this deal!"
|
||||
TALK ""
|
||||
TALK "📊 Probability: 100%"
|
||||
ELSE IF newstage = "Closed Lost" THEN
|
||||
TALK "📋 **Deal Closed Lost**"
|
||||
TALK ""
|
||||
TALK "**Deal " + dealid + "** has been marked as **Closed Lost**."
|
||||
IF reason != "" THEN
|
||||
TALK "📝 Reason: " + reason
|
||||
END IF
|
||||
TALK ""
|
||||
TALK "Don't worry - analyze what happened and apply the learnings to future deals!"
|
||||
ELSE
|
||||
TALK "✅ **Deal Stage Updated!**"
|
||||
TALK ""
|
||||
TALK "**Deal " + dealid + "** moved to **" + newstage + "**"
|
||||
TALK "📊 New Probability: " + probability + "%"
|
||||
IF reason != "" THEN
|
||||
TALK "📝 Note: " + reason
|
||||
END IF
|
||||
TALK ""
|
||||
TALK "Keep the momentum going!"
|
||||
END IF
|
||||
|
||||
' Suggest next actions based on stage
|
||||
IF newstage = "Qualified" THEN
|
||||
TALK ""
|
||||
TALK "💡 **Next Steps:**"
|
||||
TALK "• Prepare a tailored proposal"
|
||||
TALK "• Schedule a demo or presentation"
|
||||
TALK "• Identify key stakeholders"
|
||||
ELSE IF newstage = "Proposal" THEN
|
||||
TALK ""
|
||||
TALK "💡 **Next Steps:**"
|
||||
TALK "• Follow up within 48 hours"
|
||||
TALK "• Address any questions or concerns"
|
||||
TALK "• Identify decision timeline"
|
||||
ELSE IF newstage = "Negotiation" THEN
|
||||
TALK ""
|
||||
TALK "💡 **Next Steps:**"
|
||||
TALK "• Clarify any contract terms"
|
||||
TALK "• Prepare for potential objections"
|
||||
TALK "• Confirm decision makers and process"
|
||||
END IF
|
||||
158
crm/sales-pipeline.gbai/sales-pipeline.gbkb/sales-methodology.md
Normal file
158
crm/sales-pipeline.gbai/sales-pipeline.gbkb/sales-methodology.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# Sales Methodology Guide
|
||||
|
||||
## Pipeline Stages Overview
|
||||
|
||||
### 1. Lead Stage (10% Probability)
|
||||
|
||||
A lead is an initial contact or potential opportunity that has not yet been qualified.
|
||||
|
||||
**Entry Criteria:**
|
||||
- First contact made with prospect
|
||||
- Basic company information gathered
|
||||
- Initial interest expressed
|
||||
|
||||
**Activities:**
|
||||
- Research the company and contact
|
||||
- Identify potential use cases
|
||||
- Schedule discovery call
|
||||
- Gather basic requirements
|
||||
|
||||
**Exit Criteria:**
|
||||
- Discovery call completed
|
||||
- Budget, Authority, Need, Timeline (BANT) partially identified
|
||||
- Prospect agrees to next meeting
|
||||
|
||||
### 2. Qualified Stage (25% Probability)
|
||||
|
||||
A qualified opportunity has confirmed need and basic fit with our solution.
|
||||
|
||||
**Entry Criteria:**
|
||||
- BANT criteria confirmed
|
||||
- Decision maker identified
|
||||
- Clear use case defined
|
||||
- Timeline established
|
||||
|
||||
**Activities:**
|
||||
- Deep dive into requirements
|
||||
- Identify all stakeholders
|
||||
- Map decision process
|
||||
- Schedule demo or presentation
|
||||
|
||||
**Exit Criteria:**
|
||||
- Demo completed
|
||||
- Proposal requested
|
||||
- Budget confirmed
|
||||
|
||||
### 3. Proposal Stage (50% Probability)
|
||||
|
||||
A formal proposal or quote has been submitted to the prospect.
|
||||
|
||||
**Entry Criteria:**
|
||||
- Requirements documented
|
||||
- Pricing approved internally
|
||||
- Proposal sent to prospect
|
||||
|
||||
**Activities:**
|
||||
- Present proposal
|
||||
- Address questions
|
||||
- Handle objections
|
||||
- Negotiate terms if needed
|
||||
|
||||
**Exit Criteria:**
|
||||
- Proposal reviewed by prospect
|
||||
- Feedback received
|
||||
- Negotiation phase begins
|
||||
|
||||
### 4. Negotiation Stage (75% Probability)
|
||||
|
||||
Active discussions on pricing, terms, or contract details.
|
||||
|
||||
**Entry Criteria:**
|
||||
- Proposal accepted in principle
|
||||
- Negotiations on terms begun
|
||||
- Legal or procurement involved
|
||||
|
||||
**Activities:**
|
||||
- Negotiate pricing
|
||||
- Finalize contract terms
|
||||
- Address legal concerns
|
||||
- Get final sign-off
|
||||
|
||||
**Exit Criteria:**
|
||||
- Terms agreed
|
||||
- Contract ready for signature
|
||||
- Purchase order received
|
||||
|
||||
### 5. Closed Won (100% Probability)
|
||||
|
||||
Deal successfully closed and contract signed.
|
||||
|
||||
**Entry Criteria:**
|
||||
- Contract signed
|
||||
- Payment terms agreed
|
||||
- Start date confirmed
|
||||
|
||||
**Activities:**
|
||||
- Hand off to implementation
|
||||
- Send welcome materials
|
||||
- Schedule kickoff
|
||||
|
||||
### 6. Closed Lost (0% Probability)
|
||||
|
||||
Deal lost to competitor, no decision, or other reason.
|
||||
|
||||
**Common Loss Reasons:**
|
||||
- Lost to competitor
|
||||
- No budget
|
||||
- No decision / timing
|
||||
- Requirements changed
|
||||
- Contact went silent
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Deal Hygiene
|
||||
|
||||
1. **Update deals regularly** - Review and update each deal at least weekly
|
||||
2. **Accurate close dates** - Keep expected close dates realistic
|
||||
3. **Stage accuracy** - Ensure deals are in the correct stage
|
||||
4. **Notes matter** - Document all key interactions
|
||||
|
||||
### Forecasting Tips
|
||||
|
||||
- Weighted pipeline = Deal Value × Probability
|
||||
- Focus on deals closing this month
|
||||
- Review stale deals weekly
|
||||
- Be conservative with close dates
|
||||
|
||||
### Common Objections and Responses
|
||||
|
||||
**"It's too expensive"**
|
||||
- Focus on ROI and value
|
||||
- Break down cost per user
|
||||
- Compare to cost of not solving the problem
|
||||
|
||||
**"We're not ready"**
|
||||
- Understand the timing concern
|
||||
- Offer phased implementation
|
||||
- Stay in touch for future opportunity
|
||||
|
||||
**"We're evaluating competitors"**
|
||||
- Understand their criteria
|
||||
- Highlight differentiators
|
||||
- Request fair comparison opportunity
|
||||
|
||||
## Key Metrics
|
||||
|
||||
| Metric | Target | Description |
|
||||
|--------|--------|-------------|
|
||||
| Win Rate | 25%+ | Closed Won / Total Closed |
|
||||
| Avg Deal Size | Varies | Average value of won deals |
|
||||
| Sales Cycle | 30-90 days | Avg time from Lead to Close |
|
||||
| Pipeline Coverage | 3x quota | Total pipeline vs. target |
|
||||
|
||||
## Getting Help
|
||||
|
||||
- For deal strategy: Contact your sales manager
|
||||
- For pricing: Contact sales operations
|
||||
- For technical questions: Contact solutions engineering
|
||||
- For contract terms: Contact legal
|
||||
9
crm/sales-pipeline.gbai/sales-pipeline.gbot/config.csv
Normal file
9
crm/sales-pipeline.gbai/sales-pipeline.gbot/config.csv
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name,value
|
||||
episodic-memory-history,2
|
||||
episodic-memory-threshold,4
|
||||
theme-color1,#2E7D32
|
||||
theme-color2,#E8F5E9
|
||||
theme-logo,https://pragmatismo.com.br/icons/general-bots.svg
|
||||
theme-title,Sales Pipeline - General Bots
|
||||
bot-name,Sales Pipeline Assistant
|
||||
welcome-message,Welcome to the Sales Pipeline Manager!
|
||||
|
102
default.gbai/default.gbdialog/calculate.bas
Normal file
102
default.gbai/default.gbdialog/calculate.bas
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
PARAM expression AS STRING LIKE "2 + 2" DESCRIPTION "Mathematical expression to calculate"
|
||||
|
||||
DESCRIPTION "Calculate mathematical expressions, conversions, and formulas"
|
||||
|
||||
WITH result
|
||||
expression = expression
|
||||
timestamp = NOW()
|
||||
END WITH
|
||||
|
||||
expr = REPLACE(expression, " ", "")
|
||||
|
||||
IF INSTR(expr, "+") > 0 THEN
|
||||
parts = SPLIT(expr, "+")
|
||||
IF UBOUND(parts) = 2 THEN
|
||||
result.answer = VAL(parts[0]) + VAL(parts[1])
|
||||
result.operation = "addition"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(expr, "-") > 0 AND LEFT(expr, 1) <> "-" THEN
|
||||
parts = SPLIT(expr, "-")
|
||||
IF UBOUND(parts) = 2 THEN
|
||||
result.answer = VAL(parts[0]) - VAL(parts[1])
|
||||
result.operation = "subtraction"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(expr, "*") > 0 THEN
|
||||
parts = SPLIT(expr, "*")
|
||||
IF UBOUND(parts) = 2 THEN
|
||||
result.answer = VAL(parts[0]) * VAL(parts[1])
|
||||
result.operation = "multiplication"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(expr, "/") > 0 THEN
|
||||
parts = SPLIT(expr, "/")
|
||||
IF UBOUND(parts) = 2 THEN
|
||||
IF VAL(parts[1]) <> 0 THEN
|
||||
result.answer = VAL(parts[0]) / VAL(parts[1])
|
||||
result.operation = "division"
|
||||
ELSE
|
||||
TALK "Error: Division by zero"
|
||||
RETURN NULL
|
||||
END IF
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(LCASE(expr), "sqrt") > 0 THEN
|
||||
start_pos = INSTR(LCASE(expr), "sqrt(") + 5
|
||||
end_pos = INSTR(start_pos, expr, ")")
|
||||
IF end_pos > start_pos THEN
|
||||
num = VAL(MID(expr, start_pos, end_pos - start_pos))
|
||||
IF num >= 0 THEN
|
||||
result.answer = SQR(num)
|
||||
result.operation = "square root"
|
||||
ELSE
|
||||
TALK "Error: Cannot calculate square root of negative number"
|
||||
RETURN NULL
|
||||
END IF
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(expr, "^") > 0 THEN
|
||||
parts = SPLIT(expr, "^")
|
||||
IF UBOUND(parts) = 2 THEN
|
||||
result.answer = VAL(parts[0]) ^ VAL(parts[1])
|
||||
result.operation = "power"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(LCASE(expr), "abs") > 0 THEN
|
||||
start_pos = INSTR(LCASE(expr), "abs(") + 4
|
||||
end_pos = INSTR(start_pos, expr, ")")
|
||||
IF end_pos > start_pos THEN
|
||||
result.answer = ABS(VAL(MID(expr, start_pos, end_pos - start_pos)))
|
||||
result.operation = "absolute value"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(LCASE(expr), "round") > 0 THEN
|
||||
start_pos = INSTR(LCASE(expr), "round(") + 6
|
||||
end_pos = INSTR(start_pos, expr, ")")
|
||||
IF end_pos > start_pos THEN
|
||||
result.answer = ROUND(VAL(MID(expr, start_pos, end_pos - start_pos)), 0)
|
||||
result.operation = "rounding"
|
||||
END IF
|
||||
|
||||
ELSE IF INSTR(expr, "%") > 0 AND INSTR(LCASE(expr), "of") > 0 THEN
|
||||
expr_lower = LCASE(expr)
|
||||
of_pos = INSTR(expr_lower, "of")
|
||||
percent_part = REPLACE(LEFT(expr, of_pos - 1), "%", "")
|
||||
percent_val = VAL(TRIM(percent_part))
|
||||
base_val = VAL(TRIM(MID(expr, of_pos + 2)))
|
||||
result.answer = (percent_val / 100) * base_val
|
||||
result.operation = "percentage"
|
||||
|
||||
ELSE
|
||||
result.answer = VAL(expr)
|
||||
result.operation = "direct value"
|
||||
END IF
|
||||
|
||||
IF result.answer <> NULL THEN
|
||||
TALK "Result: " + result.answer
|
||||
RETURN result
|
||||
ELSE
|
||||
TALK "Could not calculate expression"
|
||||
RETURN NULL
|
||||
END IF
|
||||
26
default.gbai/default.gbdialog/send-email.bas
Normal file
26
default.gbai/default.gbdialog/send-email.bas
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
PARAM to_email AS EMAIL LIKE "user@example.com" DESCRIPTION "Recipient email address"
|
||||
PARAM subject AS STRING LIKE "Important Message" DESCRIPTION "Email subject line"
|
||||
PARAM body AS STRING LIKE "Hello, this is the email content." DESCRIPTION "Email body content"
|
||||
PARAM from_email AS EMAIL LIKE "noreply@company.com" DESCRIPTION "Sender email address" OPTIONAL
|
||||
|
||||
DESCRIPTION "Send an email to any recipient with subject and body"
|
||||
|
||||
IF NOT from_email THEN
|
||||
from_email = "noreply@pragmatismo.com.br"
|
||||
END IF
|
||||
|
||||
WITH email_data
|
||||
to = to_email
|
||||
from = from_email
|
||||
subject = subject
|
||||
body = body
|
||||
timestamp = NOW()
|
||||
END WITH
|
||||
|
||||
SEND EMAIL to_email, subject, body
|
||||
|
||||
SAVE "email_log.csv", email_data
|
||||
|
||||
TALK "Email sent to " + to_email
|
||||
|
||||
RETURN email_data
|
||||
28
default.gbai/default.gbdialog/send-sms.bas
Normal file
28
default.gbai/default.gbdialog/send-sms.bas
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
PARAM phone_number AS PHONE LIKE "+1234567890" DESCRIPTION "Phone number with country code"
|
||||
PARAM message AS STRING LIKE "Hello, this is your message" DESCRIPTION "SMS message content"
|
||||
PARAM from_number AS PHONE LIKE "+1987654321" DESCRIPTION "Sender phone number" OPTIONAL
|
||||
|
||||
DESCRIPTION "Send an SMS message to any phone number"
|
||||
|
||||
message_length = LEN(message)
|
||||
segments = INT((message_length - 1) / 160) + 1
|
||||
|
||||
IF message_length > 160 THEN
|
||||
TALK "Message will be split into " + segments + " segments"
|
||||
END IF
|
||||
|
||||
WITH sms
|
||||
to = phone_number
|
||||
from = from_number
|
||||
body = message
|
||||
timestamp = NOW()
|
||||
segmentCount = segments
|
||||
END WITH
|
||||
|
||||
SEND SMS phone_number, message
|
||||
|
||||
SAVE "sms_log.csv", sms
|
||||
|
||||
TALK "SMS sent to " + phone_number
|
||||
|
||||
RETURN sms
|
||||
60
default.gbai/default.gbdialog/translate.bas
Normal file
60
default.gbai/default.gbdialog/translate.bas
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
PARAM text AS STRING LIKE "Hello, how are you?" DESCRIPTION "Text to translate"
|
||||
PARAM from_lang AS STRING LIKE "en" DESCRIPTION "Source language code (en, es, pt, fr, de, etc)" OPTIONAL
|
||||
PARAM to_lang AS STRING LIKE "es" DESCRIPTION "Target language code (en, es, pt, fr, de, etc)" OPTIONAL
|
||||
|
||||
DESCRIPTION "Translate text between languages using free translation API"
|
||||
|
||||
IF NOT from_lang THEN
|
||||
from_lang = "en"
|
||||
END IF
|
||||
|
||||
IF NOT to_lang THEN
|
||||
to_lang = "es"
|
||||
END IF
|
||||
|
||||
TALK "Translating from " + from_lang + " to " + to_lang + "..."
|
||||
|
||||
WITH post_data
|
||||
q = text
|
||||
source = from_lang
|
||||
target = to_lang
|
||||
format = "text"
|
||||
END WITH
|
||||
|
||||
SET HEADER "Content-Type" = "application/json"
|
||||
|
||||
translation_result = POST "https://libretranslate.com/translate", post_data
|
||||
|
||||
IF translation_result.translatedText THEN
|
||||
WITH result
|
||||
original = text
|
||||
translated = translation_result.translatedText
|
||||
from = from_lang
|
||||
to = to_lang
|
||||
END WITH
|
||||
|
||||
TALK "Original (" + from_lang + "): " + text
|
||||
TALK "Translated (" + to_lang + "): " + result.translated
|
||||
|
||||
RETURN result
|
||||
ELSE
|
||||
mymemory_url = "https://api.mymemory.translated.net/get?q=" + text + "&langpair=" + from_lang + "|" + to_lang
|
||||
fallback_result = GET mymemory_url
|
||||
|
||||
IF fallback_result.responseData.translatedText THEN
|
||||
WITH result
|
||||
original = text
|
||||
translated = fallback_result.responseData.translatedText
|
||||
from = from_lang
|
||||
to = to_lang
|
||||
END WITH
|
||||
|
||||
TALK "Original (" + from_lang + "): " + text
|
||||
TALK "Translated (" + to_lang + "): " + result.translated
|
||||
|
||||
RETURN result
|
||||
ELSE
|
||||
TALK "Could not translate text"
|
||||
RETURN NULL
|
||||
END IF
|
||||
END IF
|
||||
112
default.gbai/default.gbdialog/weather.bas
Normal file
112
default.gbai/default.gbdialog/weather.bas
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
PARAM location AS STRING LIKE "New York" DESCRIPTION "City or location to get weather forecast"
|
||||
|
||||
DESCRIPTION "Get current weather forecast for any city or location"
|
||||
|
||||
lat = 40.7128
|
||||
lon = -74.0060
|
||||
|
||||
location_lower = LCASE(location)
|
||||
|
||||
IF INSTR(location_lower, "new york") > 0 THEN
|
||||
lat = 40.7128
|
||||
lon = -74.0060
|
||||
ELSE IF INSTR(location_lower, "london") > 0 THEN
|
||||
lat = 51.5074
|
||||
lon = -0.1278
|
||||
ELSE IF INSTR(location_lower, "paris") > 0 THEN
|
||||
lat = 48.8566
|
||||
lon = 2.3522
|
||||
ELSE IF INSTR(location_lower, "tokyo") > 0 THEN
|
||||
lat = 35.6762
|
||||
lon = 139.6503
|
||||
ELSE IF INSTR(location_lower, "sydney") > 0 THEN
|
||||
lat = -33.8688
|
||||
lon = 151.2093
|
||||
ELSE IF INSTR(location_lower, "berlin") > 0 THEN
|
||||
lat = 52.5200
|
||||
lon = 13.4050
|
||||
ELSE IF INSTR(location_lower, "madrid") > 0 THEN
|
||||
lat = 40.4168
|
||||
lon = -3.7038
|
||||
ELSE IF INSTR(location_lower, "sao paulo") > 0 OR INSTR(location_lower, "são paulo") > 0 THEN
|
||||
lat = -23.5505
|
||||
lon = -46.6333
|
||||
ELSE IF INSTR(location_lower, "rio") > 0 THEN
|
||||
lat = -22.9068
|
||||
lon = -43.1729
|
||||
ELSE IF INSTR(location_lower, "los angeles") > 0 THEN
|
||||
lat = 34.0522
|
||||
lon = -118.2437
|
||||
ELSE IF INSTR(location_lower, "chicago") > 0 THEN
|
||||
lat = 41.8781
|
||||
lon = -87.6298
|
||||
ELSE IF INSTR(location_lower, "toronto") > 0 THEN
|
||||
lat = 43.6532
|
||||
lon = -79.3832
|
||||
ELSE IF INSTR(location_lower, "dubai") > 0 THEN
|
||||
lat = 25.2048
|
||||
lon = 55.2708
|
||||
ELSE IF INSTR(location_lower, "singapore") > 0 THEN
|
||||
lat = 1.3521
|
||||
lon = 103.8198
|
||||
ELSE IF INSTR(location_lower, "mumbai") > 0 THEN
|
||||
lat = 19.0760
|
||||
lon = 72.8777
|
||||
ELSE IF INSTR(location_lower, "beijing") > 0 THEN
|
||||
lat = 39.9042
|
||||
lon = 116.4074
|
||||
END IF
|
||||
|
||||
weather_url = "https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&timezone=auto"
|
||||
|
||||
weather_data = GET weather_url
|
||||
|
||||
IF weather_data.current_weather THEN
|
||||
current = weather_data.current_weather
|
||||
|
||||
code = current.weathercode
|
||||
condition = "Clear"
|
||||
icon = "☀️"
|
||||
|
||||
IF code = 0 THEN
|
||||
condition = "Clear sky"
|
||||
icon = "☀️"
|
||||
ELSE IF code >= 1 AND code <= 3 THEN
|
||||
condition = "Partly cloudy"
|
||||
icon = "⛅"
|
||||
ELSE IF code >= 45 AND code <= 48 THEN
|
||||
condition = "Foggy"
|
||||
icon = "🌫️"
|
||||
ELSE IF code >= 51 AND code <= 67 THEN
|
||||
condition = "Rainy"
|
||||
icon = "🌧️"
|
||||
ELSE IF code >= 71 AND code <= 77 THEN
|
||||
condition = "Snowy"
|
||||
icon = "❄️"
|
||||
ELSE IF code >= 80 AND code <= 82 THEN
|
||||
condition = "Rain showers"
|
||||
icon = "🌦️"
|
||||
ELSE IF code >= 95 AND code <= 99 THEN
|
||||
condition = "Thunderstorm"
|
||||
icon = "⛈️"
|
||||
END IF
|
||||
|
||||
WITH result
|
||||
loc = location
|
||||
temperature = current.temperature
|
||||
windspeed = current.windspeed
|
||||
weathercode = code
|
||||
cond = condition
|
||||
ico = icon
|
||||
END WITH
|
||||
|
||||
TALK icon + " Weather for " + location + ":"
|
||||
TALK "Temperature: " + current.temperature + "°C"
|
||||
TALK "Condition: " + condition
|
||||
TALK "Wind: " + current.windspeed + " km/h"
|
||||
|
||||
RETURN result
|
||||
ELSE
|
||||
TALK "Could not fetch weather for: " + location
|
||||
RETURN NULL
|
||||
END IF
|
||||
127
default.gbai/default.gbkb/getting-started.md
Normal file
127
default.gbai/default.gbkb/getting-started.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Getting Started with General Bots
|
||||
|
||||
## Overview
|
||||
|
||||
Welcome to General Bots! This guide will help you understand the basic features available in your default bot installation.
|
||||
|
||||
## Available Features
|
||||
|
||||
### Calculator
|
||||
|
||||
Perform mathematical calculations by asking the bot to calculate expressions.
|
||||
|
||||
**Examples:**
|
||||
- "Calculate 25 * 4"
|
||||
- "What is 1500 / 12?"
|
||||
- "Calculate 15% of 200"
|
||||
|
||||
### Send Email
|
||||
|
||||
Send emails directly through the bot.
|
||||
|
||||
**How to use:**
|
||||
1. Say "Send email" or "Send an email"
|
||||
2. Provide the recipient's email address
|
||||
3. Enter the subject line
|
||||
4. Type your message content
|
||||
|
||||
**Example:**
|
||||
- "Send an email to john@example.com"
|
||||
- "I need to email my team"
|
||||
|
||||
### Send SMS
|
||||
|
||||
Send text messages to mobile phones.
|
||||
|
||||
**How to use:**
|
||||
1. Say "Send SMS" or "Send a text message"
|
||||
2. Provide the phone number (with country code)
|
||||
3. Enter your message
|
||||
|
||||
**Example:**
|
||||
- "Send SMS to +1234567890"
|
||||
- "Text message to my contact"
|
||||
|
||||
### Translation
|
||||
|
||||
Translate text between different languages.
|
||||
|
||||
**How to use:**
|
||||
1. Say "Translate" followed by the text
|
||||
2. Specify the target language
|
||||
|
||||
**Examples:**
|
||||
- "Translate 'Hello, how are you?' to Spanish"
|
||||
- "Translate this text to Portuguese"
|
||||
- "How do you say 'thank you' in French?"
|
||||
|
||||
### Weather
|
||||
|
||||
Get current weather information for any location.
|
||||
|
||||
**How to use:**
|
||||
1. Ask about the weather for a specific location
|
||||
|
||||
**Examples:**
|
||||
- "What's the weather in New York?"
|
||||
- "Weather forecast for London"
|
||||
- "Is it going to rain in Tokyo?"
|
||||
|
||||
## Tips for Better Interactions
|
||||
|
||||
### Be Specific
|
||||
The more specific your request, the better the bot can help you. Include relevant details like:
|
||||
- Email addresses for sending emails
|
||||
- Phone numbers with country codes for SMS
|
||||
- City names for weather queries
|
||||
|
||||
### Natural Language
|
||||
You can speak naturally to the bot. It understands various ways of asking for the same thing:
|
||||
- "Calculate 10 + 5" or "What is 10 plus 5?"
|
||||
- "Send email" or "I need to email someone"
|
||||
- "Translate to Spanish" or "How do you say this in Spanish?"
|
||||
|
||||
### Confirmation
|
||||
The bot will ask for confirmation before performing actions like sending emails or SMS to ensure accuracy.
|
||||
|
||||
## Extending Your Bot
|
||||
|
||||
This default template provides basic functionality. You can extend your bot by:
|
||||
|
||||
1. **Adding Knowledge Base**: Create `.md` files in the `.gbkb` folder to give your bot domain-specific knowledge
|
||||
2. **Creating Dialogs**: Add `.bas` files in the `.gbdialog` folder for custom conversations
|
||||
3. **Installing Templates**: Add pre-built templates for CRM, HR, helpdesk, and more
|
||||
4. **Connecting APIs**: Integrate external services for expanded functionality
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I add more features to my bot?**
|
||||
A: Install additional templates or create custom dialog scripts in the `.gbdialog` folder.
|
||||
|
||||
**Q: Can the bot remember previous conversations?**
|
||||
A: Yes, the bot maintains context within a session. For persistent memory, use the memory features in custom dialogs.
|
||||
|
||||
**Q: What languages are supported?**
|
||||
A: The bot supports multiple languages for both interface and translation. Common languages include English, Portuguese, Spanish, French, German, and many others.
|
||||
|
||||
**Q: How do I change the bot's appearance?**
|
||||
A: Modify the `config.csv` file in the `.gbot` folder to change colors, logo, and title.
|
||||
|
||||
**Q: Is my data secure?**
|
||||
A: Yes, all communications are encrypted. Sensitive data like passwords should never be shared in chat.
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you need assistance:
|
||||
- Ask the bot "Help" for available commands
|
||||
- Check the documentation at docs.pragmatismo.com.br
|
||||
- Contact support for technical issues
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Try out each feature to see how it works
|
||||
2. Explore the template library for pre-built solutions
|
||||
3. Customize your bot with your own knowledge base
|
||||
4. Create custom dialogs for your specific use cases
|
||||
|
||||
Welcome aboard, and enjoy using General Bots!
|
||||
228
default.gbai/default.gbot/config.csv
Normal file
228
default.gbai/default.gbot/config.csv
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
name,value
|
||||
,
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
server_host,0.0.0.0
|
||||
server_port,8088
|
||||
sites_root,/tmp
|
||||
,
|
||||
# ============================================================================
|
||||
# LLM CONFIGURATION
|
||||
# ============================================================================
|
||||
llm-key,none
|
||||
llm-url,http://localhost:8081
|
||||
llm-model,../../../../data/llm/DeepSeek-R1-Distill-Qwen-1.5B-Q3_K_M.gguf
|
||||
,
|
||||
llm-cache,false
|
||||
llm-cache-ttl,3600
|
||||
llm-cache-semantic,true
|
||||
llm-cache-threshold,0.95
|
||||
,
|
||||
episodic-memory-threshold,4
|
||||
,
|
||||
mcp-server,false
|
||||
,
|
||||
# ============================================================================
|
||||
# EMBEDDING CONFIGURATION
|
||||
# ============================================================================
|
||||
embedding-url,http://localhost:8082
|
||||
embedding-model,../../../../data/llm/bge-small-en-v1.5-f32.gguf
|
||||
,
|
||||
# ============================================================================
|
||||
# LLM SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
llm-server,true
|
||||
llm-server-path,botserver-stack/bin/llm/build/bin
|
||||
llm-server-host,0.0.0.0
|
||||
llm-server-port,8081
|
||||
llm-server-gpu-layers,0
|
||||
llm-server-n-moe,0
|
||||
llm-server-ctx-size,4096
|
||||
llm-server-n-predict,1024
|
||||
llm-server-parallel,6
|
||||
llm-server-cont-batching,true
|
||||
llm-server-mlock,false
|
||||
llm-server-no-mmap,false
|
||||
,
|
||||
# ============================================================================
|
||||
# EMAIL CONFIGURATION
|
||||
# ============================================================================
|
||||
email-from,from@domain.com
|
||||
email-server,mail.domain.com
|
||||
email-port,587
|
||||
email-user,user@domain.com
|
||||
email-pass,
|
||||
,
|
||||
# ============================================================================
|
||||
# DATABASE CONFIGURATION
|
||||
# ============================================================================
|
||||
custom-server,localhost
|
||||
custom-port,5432
|
||||
custom-database,mycustomdb
|
||||
custom-username,
|
||||
custom-password,
|
||||
,
|
||||
# ============================================================================
|
||||
# WEBSITE CRAWLER CONFIGURATION
|
||||
# ============================================================================
|
||||
website-expires,1d
|
||||
website-max-depth,3
|
||||
website-max-pages,100
|
||||
,
|
||||
# ============================================================================
|
||||
# IMAGE GENERATOR CONFIGURATION
|
||||
# ============================================================================
|
||||
image-generator-model,../../../../data/diffusion/sd_turbo_f16.gguf
|
||||
image-generator-steps,4
|
||||
image-generator-width,512
|
||||
image-generator-height,512
|
||||
image-generator-gpu-layers,20
|
||||
image-generator-batch-size,1
|
||||
,
|
||||
# ============================================================================
|
||||
# VIDEO GENERATOR CONFIGURATION
|
||||
# ============================================================================
|
||||
video-generator-model,../../../../data/diffusion/zeroscope_v2_576w
|
||||
video-generator-frames,24
|
||||
video-generator-fps,8
|
||||
video-generator-width,320
|
||||
video-generator-height,576
|
||||
video-generator-gpu-layers,15
|
||||
video-generator-batch-size,1
|
||||
,
|
||||
# ============================================================================
|
||||
# BOTMODELS CONFIGURATION
|
||||
# ============================================================================
|
||||
botmodels-enabled,true
|
||||
botmodels-host,0.0.0.0
|
||||
botmodels-port,8085
|
||||
,
|
||||
default-generator,all
|
||||
,
|
||||
# ============================================================================
|
||||
# OAUTH AUTHENTICATION CONFIGURATION
|
||||
# ============================================================================
|
||||
# Enable social login providers by setting the corresponding -enabled flag
|
||||
# to "true" and providing valid client credentials.
|
||||
#
|
||||
# Each provider requires:
|
||||
# - oauth-{provider}-enabled: Set to "true" to enable the provider
|
||||
# - oauth-{provider}-client-id: The Client ID from the provider
|
||||
# - oauth-{provider}-client-secret: The Client Secret from the provider
|
||||
# - oauth-{provider}-redirect-uri: (Optional) Custom callback URL
|
||||
#
|
||||
# Default redirect URI format: http://your-domain/auth/oauth/{provider}/callback
|
||||
# ============================================================================
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# GOOGLE OAUTH
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://console.cloud.google.com/apis/credentials
|
||||
# 2. Create a new project or select existing
|
||||
# 3. Click "Create Credentials" > "OAuth client ID"
|
||||
# 4. Select "Web application" as application type
|
||||
# 5. Add authorized redirect URI: http://your-domain/auth/oauth/google/callback
|
||||
# 6. Copy the Client ID and Client Secret below
|
||||
#
|
||||
# Documentation: https://developers.google.com/identity/protocols/oauth2/web-server
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-google-enabled,false
|
||||
oauth-google-client-id,
|
||||
oauth-google-client-secret,
|
||||
oauth-google-redirect-uri,
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# MICROSOFT OAUTH (Azure AD)
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
|
||||
# 2. Click "New registration"
|
||||
# 3. Enter application name and select "Accounts in any organizational directory and personal Microsoft accounts"
|
||||
# 4. Add redirect URI: http://your-domain/auth/oauth/microsoft/callback (Web platform)
|
||||
# 5. Go to "Certificates & secrets" > "New client secret"
|
||||
# 6. Copy the Application (client) ID and secret value below
|
||||
#
|
||||
# Documentation: https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-microsoft-enabled,false
|
||||
oauth-microsoft-client-id,
|
||||
oauth-microsoft-client-secret,
|
||||
oauth-microsoft-redirect-uri,
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# DISCORD OAUTH
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://discord.com/developers/applications
|
||||
# 2. Click "New Application" and enter a name
|
||||
# 3. Go to "OAuth2" in the left sidebar
|
||||
# 4. Add redirect URL: http://your-domain/auth/oauth/discord/callback
|
||||
# 5. Copy the Client ID and Client Secret below
|
||||
# 6. Under "OAuth2 URL Generator", select scopes: identify, email
|
||||
#
|
||||
# Documentation: https://discord.com/developers/docs/topics/oauth2
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-discord-enabled,false
|
||||
oauth-discord-client-id,
|
||||
oauth-discord-client-secret,
|
||||
oauth-discord-redirect-uri,
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# FACEBOOK OAUTH
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://developers.facebook.com/apps/
|
||||
# 2. Click "Create App" > Select "Consumer" or "Business"
|
||||
# 3. Add "Facebook Login" product to your app
|
||||
# 4. Go to Facebook Login > Settings
|
||||
# 5. Add Valid OAuth Redirect URI: http://your-domain/auth/oauth/facebook/callback
|
||||
# 6. Go to Settings > Basic to get App ID and App Secret
|
||||
#
|
||||
# Documentation: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-facebook-enabled,false
|
||||
oauth-facebook-client-id,
|
||||
oauth-facebook-client-secret,
|
||||
oauth-facebook-redirect-uri,
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# TWITTER (X) OAUTH 2.0
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://developer.twitter.com/en/portal/dashboard
|
||||
# 2. Create a new project and app (or use existing)
|
||||
# 3. Go to your app's "Keys and tokens" tab
|
||||
# 4. Under "OAuth 2.0 Client ID and Client Secret", generate credentials
|
||||
# 5. Go to "User authentication settings" and configure:
|
||||
# - Enable OAuth 2.0
|
||||
# - Type: Web App
|
||||
# - Callback URL: http://your-domain/auth/oauth/twitter/callback
|
||||
# 6. Copy Client ID and Client Secret below
|
||||
#
|
||||
# Note: Twitter requires OAuth 2.0 with PKCE for web apps
|
||||
# Documentation: https://developer.twitter.com/en/docs/authentication/oauth-2-0
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-twitter-enabled,false
|
||||
oauth-twitter-client-id,
|
||||
oauth-twitter-client-secret,
|
||||
oauth-twitter-redirect-uri,
|
||||
,
|
||||
# ----------------------------------------------------------------------------
|
||||
# REDDIT OAUTH
|
||||
# ----------------------------------------------------------------------------
|
||||
# Setup Instructions:
|
||||
# 1. Go to https://www.reddit.com/prefs/apps
|
||||
# 2. Click "create another app..." at the bottom
|
||||
# 3. Select "web app" as the application type
|
||||
# 4. Enter redirect URI: http://your-domain/auth/oauth/reddit/callback
|
||||
# 5. Copy the client ID (under app name) and secret below
|
||||
#
|
||||
# Note: Reddit requires Basic Auth for token exchange and custom User-Agent
|
||||
# Documentation: https://github.com/reddit-archive/reddit/wiki/OAuth2
|
||||
# ----------------------------------------------------------------------------
|
||||
oauth-reddit-enabled,false
|
||||
oauth-reddit-client-id,
|
||||
oauth-reddit-client-secret,
|
||||
oauth-reddit-redirect-uri,
|
||||
|
Can't render this file because it contains an unexpected character in line 107 and column 6.
|
1043
drive.html
Normal file
1043
drive.html
Normal file
File diff suppressed because it is too large
Load diff
3
edu.gbai/edu.gbdata/enrollments.csv
Normal file
3
edu.gbai/edu.gbdata/enrollments.csv
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Id,Name,Birthday,Email,Personalid,Address
|
||||
lwkerderv,John Godf,12/12/2001,johng@fool.com.tg,12381239923,"Boulevard Street, 329"
|
||||
ekelwbctw,Jorge Santos,12/01/1978,jorge@uol.com.br,1239892998,"Rua Teodoro, 39"
|
||||
|
35
edu.gbai/edu.gbdialog/enrollment.bas
Normal file
35
edu.gbai/edu.gbdialog/enrollment.bas
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
PARAM name AS STRING LIKE "Abreu Silva" DESCRIPTION "Full name of the student"
|
||||
PARAM birthday AS DATE LIKE "23/09/2001" DESCRIPTION "Birth date in DD/MM/YYYY format"
|
||||
PARAM email AS EMAIL LIKE "abreu.silva@example.com" DESCRIPTION "Email address for contact"
|
||||
PARAM personalid AS STRING LIKE "12345678900" DESCRIPTION "Personal ID number (only numbers)"
|
||||
PARAM address AS STRING LIKE "Rua das Flores, 123 - SP" DESCRIPTION "Full address"
|
||||
|
||||
DESCRIPTION "Process student enrollment with validation and confirmation"
|
||||
|
||||
enrollmentid = "ENR-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
createdat = FORMAT(NOW(), "YYYY-MM-DD HH:mm:ss")
|
||||
|
||||
WITH enrollment
|
||||
id = enrollmentid
|
||||
studentName = name
|
||||
birthDate = birthday
|
||||
emailAddress = email
|
||||
personalId = personalid
|
||||
fullAddress = address
|
||||
createdAt = createdat
|
||||
status = "pending"
|
||||
END WITH
|
||||
|
||||
SAVE "enrollments.csv", enrollment
|
||||
|
||||
SET BOT MEMORY "last_enrollment", enrollmentid
|
||||
|
||||
TALK "Enrollment submitted successfully!"
|
||||
TALK "Enrollment ID: " + enrollmentid
|
||||
TALK "Name: " + name
|
||||
TALK "Email: " + email
|
||||
TALK "Status: Pending review"
|
||||
|
||||
SEND EMAIL email, "Enrollment Confirmation", "Dear " + name + ",\n\nYour enrollment request has been submitted.\n\nEnrollment ID: " + enrollmentid + "\n\nWe will review your application and contact you soon.\n\nBest regards,\nAdmissions Team"
|
||||
|
||||
RETURN enrollmentid
|
||||
46
edu.gbai/edu.gbdialog/start.bas
Normal file
46
edu.gbai/edu.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
ADD TOOL "enrollment"
|
||||
ADD TOOL "course-info"
|
||||
ADD TOOL "schedule"
|
||||
ADD TOOL "grades"
|
||||
ADD TOOL "tuition"
|
||||
ADD TOOL "support"
|
||||
|
||||
USE KB "edu.gbkb"
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "enroll" AS "Enroll in a course"
|
||||
ADD SUGGESTION "courses" AS "View available courses"
|
||||
ADD SUGGESTION "schedule" AS "My class schedule"
|
||||
ADD SUGGESTION "grades" AS "Check my grades"
|
||||
ADD SUGGESTION "tuition" AS "Payment information"
|
||||
ADD SUGGESTION "help" AS "Academic support"
|
||||
|
||||
SET CONTEXT "education" AS "You are an educational institution assistant helping with enrollment, courses, schedules, grades, and academic support. Be helpful and guide students through processes clearly."
|
||||
|
||||
BEGIN TALK
|
||||
**Education Assistant**
|
||||
|
||||
Welcome! I can help you with:
|
||||
• Course enrollment and registration
|
||||
• Available courses and programs
|
||||
• Class schedules and calendars
|
||||
• Grades and transcripts
|
||||
• Tuition and payment info
|
||||
• Academic support and advising
|
||||
|
||||
Select an option or ask me anything.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are an AI assistant for an educational institution.
|
||||
|
||||
Be friendly and professional.
|
||||
Provide clear, accurate assistance.
|
||||
Reduce administrative workload by handling common inquiries.
|
||||
Help with enrollment and registration.
|
||||
Provide course information and prerequisites.
|
||||
Answer admissions questions.
|
||||
Guide through registration process.
|
||||
Explain academic policies.
|
||||
END SYSTEM PROMPT
|
||||
236
edu.gbai/edu.gbkb/student-guide.md
Normal file
236
edu.gbai/edu.gbkb/student-guide.md
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
# Student Guide
|
||||
|
||||
## Welcome to Our Educational Institution
|
||||
|
||||
This guide will help you navigate our services and make the most of your educational experience.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### New Student Checklist
|
||||
|
||||
1. **Complete Registration**: Submit all required documents
|
||||
2. **Activate Your Account**: Set up your student portal access
|
||||
3. **Review Course Catalog**: Explore available courses and programs
|
||||
4. **Meet Your Advisor**: Schedule an initial advising session
|
||||
5. **Attend Orientation**: Learn about campus resources and policies
|
||||
|
||||
### Required Documents
|
||||
|
||||
- Official transcripts from previous institutions
|
||||
- Government-issued photo ID
|
||||
- Proof of residency (if applicable)
|
||||
- Vaccination records
|
||||
- Financial aid documentation (if applicable)
|
||||
|
||||
## Enrollment and Registration
|
||||
|
||||
### How to Enroll
|
||||
|
||||
1. Log into the student portal
|
||||
2. Select "Enrollment" or ask this assistant "Enroll in a course"
|
||||
3. Search for your desired course
|
||||
4. Check prerequisites and availability
|
||||
5. Add the course to your schedule
|
||||
6. Confirm enrollment
|
||||
|
||||
### Registration Periods
|
||||
|
||||
| Period | Dates | Who Can Register |
|
||||
|--------|-------|------------------|
|
||||
| Priority | 2 weeks before term | Seniors, Honors students |
|
||||
| General | 1 week before term | All students |
|
||||
| Late | First week of term | With advisor approval |
|
||||
| Add/Drop | First 2 weeks | All enrolled students |
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Some courses require completion of other courses first. To check prerequisites:
|
||||
- View the course description in the catalog
|
||||
- Ask this assistant about specific course requirements
|
||||
- Consult with your academic advisor
|
||||
|
||||
## Academic Programs
|
||||
|
||||
### Degree Types
|
||||
|
||||
- **Certificate Programs**: 6-12 months, focused skills training
|
||||
- **Associate Degree**: 2 years, foundational education
|
||||
- **Bachelor's Degree**: 4 years, comprehensive education
|
||||
- **Master's Degree**: 1-2 years post-bachelor's
|
||||
- **Doctoral Degree**: 3-5 years post-master's
|
||||
|
||||
### Majors and Concentrations
|
||||
|
||||
Contact your academic advisor or ask this assistant to explore:
|
||||
- Available majors in your school
|
||||
- Concentration options within majors
|
||||
- Minor programs
|
||||
- Dual degree opportunities
|
||||
|
||||
## Class Schedules
|
||||
|
||||
### Viewing Your Schedule
|
||||
|
||||
Ask this assistant "My class schedule" or log into the student portal to see:
|
||||
- Course names and codes
|
||||
- Meeting times and locations
|
||||
- Instructor information
|
||||
- Important dates (exams, project due dates)
|
||||
|
||||
### Schedule Formats
|
||||
|
||||
| Format | Description | Best For |
|
||||
|--------|-------------|----------|
|
||||
| In-Person | Traditional classroom | Hands-on learning |
|
||||
| Online Synchronous | Live virtual classes | Remote with interaction |
|
||||
| Online Asynchronous | Self-paced online | Flexible schedules |
|
||||
| Hybrid | Mix of in-person and online | Balanced approach |
|
||||
|
||||
## Grades and Transcripts
|
||||
|
||||
### Grading Scale
|
||||
|
||||
| Grade | Points | Percentage |
|
||||
|-------|--------|------------|
|
||||
| A | 4.0 | 90-100% |
|
||||
| B | 3.0 | 80-89% |
|
||||
| C | 2.0 | 70-79% |
|
||||
| D | 1.0 | 60-69% |
|
||||
| F | 0.0 | Below 60% |
|
||||
|
||||
### Checking Your Grades
|
||||
|
||||
- Ask "Check my grades" to this assistant
|
||||
- Log into the student portal
|
||||
- View grade reports by term
|
||||
|
||||
### Requesting Transcripts
|
||||
|
||||
Official transcripts can be requested for:
|
||||
- Graduate school applications
|
||||
- Employment verification
|
||||
- Transfer to other institutions
|
||||
|
||||
Processing time: 3-5 business days
|
||||
|
||||
## Tuition and Financial Aid
|
||||
|
||||
### Payment Options
|
||||
|
||||
1. **Full Payment**: Pay entire balance before term starts
|
||||
2. **Payment Plan**: Spread payments across the term
|
||||
3. **Financial Aid**: Grants, scholarships, and loans
|
||||
4. **Employer Reimbursement**: Deferred payment with employer letter
|
||||
|
||||
### Financial Aid Types
|
||||
|
||||
- **Grants**: Free money based on need (no repayment)
|
||||
- **Scholarships**: Merit-based awards (no repayment)
|
||||
- **Work-Study**: Part-time campus employment
|
||||
- **Loans**: Borrowed funds (must be repaid)
|
||||
|
||||
### Important Deadlines
|
||||
|
||||
- FAFSA Priority Deadline: March 1
|
||||
- Scholarship Applications: Varies by award
|
||||
- Tuition Payment: 2 weeks before term start
|
||||
- Payment Plan Enrollment: 1 month before term
|
||||
|
||||
## Academic Support Services
|
||||
|
||||
### Tutoring
|
||||
|
||||
Free tutoring available for:
|
||||
- Writing assistance
|
||||
- Math and science help
|
||||
- Study skills coaching
|
||||
- Test preparation
|
||||
|
||||
### Library Services
|
||||
|
||||
- Research databases and journals
|
||||
- Study rooms (reservable)
|
||||
- Interlibrary loan
|
||||
- Research librarian consultations
|
||||
|
||||
### Academic Advising
|
||||
|
||||
Your advisor can help with:
|
||||
- Course selection
|
||||
- Degree planning
|
||||
- Career guidance
|
||||
- Academic concerns
|
||||
|
||||
## Campus Resources
|
||||
|
||||
### Student Services
|
||||
|
||||
- Career Center
|
||||
- Counseling Services
|
||||
- Health Center
|
||||
- Disability Services
|
||||
- International Student Office
|
||||
|
||||
### Technology Resources
|
||||
|
||||
- Campus WiFi (eduroam)
|
||||
- Computer labs
|
||||
- Software downloads
|
||||
- IT help desk
|
||||
|
||||
## Academic Policies
|
||||
|
||||
### Attendance
|
||||
|
||||
- Regular attendance is expected
|
||||
- Excessive absences may affect grades
|
||||
- Notify instructors of planned absences
|
||||
- Medical documentation may be required for extended absence
|
||||
|
||||
### Academic Integrity
|
||||
|
||||
All students must:
|
||||
- Submit original work
|
||||
- Properly cite sources
|
||||
- Not share exam answers
|
||||
- Report academic misconduct
|
||||
|
||||
Violations may result in course failure or dismissal.
|
||||
|
||||
### Withdrawal Policy
|
||||
|
||||
- Full refund: Before term starts
|
||||
- 75% refund: Week 1
|
||||
- 50% refund: Week 2
|
||||
- 25% refund: Week 3
|
||||
- No refund: After week 3
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I change my major?**
|
||||
A: Schedule a meeting with an academic advisor to discuss requirements and submit a change of major form.
|
||||
|
||||
**Q: Can I take courses at another institution?**
|
||||
A: Yes, with prior approval. Submit a transient student form before registering elsewhere.
|
||||
|
||||
**Q: What is the minimum GPA requirement?**
|
||||
A: You must maintain a 2.0 GPA for good academic standing. Some programs require higher GPAs.
|
||||
|
||||
**Q: How do I appeal a grade?**
|
||||
A: First discuss with your instructor. If unresolved, submit a formal grade appeal within 30 days of grade posting.
|
||||
|
||||
**Q: Can I take a leave of absence?**
|
||||
A: Yes, submit a leave of absence request to the registrar. Leaves are typically approved for up to one year.
|
||||
|
||||
**Q: How do I get accommodations for a disability?**
|
||||
A: Contact Disability Services with documentation. They will work with you to arrange appropriate accommodations.
|
||||
|
||||
## Getting Help
|
||||
|
||||
For immediate assistance:
|
||||
- Ask this assistant any question
|
||||
- Visit the Student Services office
|
||||
- Email: studenthelp@institution.edu
|
||||
- Phone: 1-800-STU-DENT
|
||||
|
||||
We're here to support your academic success!
|
||||
4
edu.gbai/edu.gbot/config.csv
Normal file
4
edu.gbai/edu.gbot/config.csv
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name,value
|
||||
Answer Mode,tool
|
||||
Start Dialog,start
|
||||
Theme Color, indigo
|
||||
|
293
erp.gbai/erp.gbdialog/erp-jobs.bas
Normal file
293
erp.gbai/erp.gbdialog/erp-jobs.bas
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
PARAM job_name AS STRING
|
||||
|
||||
user_id = GET "session.user_id"
|
||||
current_time = FORMAT NOW() AS "YYYY-MM-DD HH:mm:ss"
|
||||
|
||||
IF job_name = "inventory_reorder" THEN
|
||||
items = FIND "items", "is_purchasable = true AND reorder_point > 0"
|
||||
|
||||
reorders_created = 0
|
||||
|
||||
FOR EACH item IN items DO
|
||||
stocks = FIND "inventory_stock", "item_id = '" + item.id + "'"
|
||||
|
||||
total_available = 0
|
||||
FOR EACH stock IN stocks DO
|
||||
total_available = total_available + stock.quantity_available
|
||||
END FOR
|
||||
|
||||
IF total_available <= item.reorder_point THEN
|
||||
po = CREATE OBJECT
|
||||
SET po.id = FORMAT GUID()
|
||||
SET po.po_number = "PO-AUTO-" + FORMAT NOW() AS "YYYYMMDD" + "-" + FORMAT RANDOM(100, 999)
|
||||
SET po.status = "draft"
|
||||
SET po.order_date = current_time
|
||||
SET po.buyer_id = "system"
|
||||
SET po.created_by = "system"
|
||||
SET po.created_at = current_time
|
||||
|
||||
vendor_item = FIND "vendor_items", "item_id = '" + item.id + "' AND is_preferred = true"
|
||||
IF vendor_item != NULL THEN
|
||||
po.vendor_id = vendor_item.vendor_id
|
||||
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_orders", FORMAT po AS JSON
|
||||
|
||||
line = CREATE OBJECT
|
||||
SET line.id = FORMAT GUID()
|
||||
SET line.po_id = po.id
|
||||
SET line.line_number = 1
|
||||
SET line.item_id = item.id
|
||||
SET line.quantity_ordered = item.reorder_quantity
|
||||
SET line.unit_price = vendor_item.unit_price
|
||||
SET line.created_at = current_time
|
||||
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_order_lines", FORMAT line AS JSON
|
||||
|
||||
reorders_created = reorders_created + 1
|
||||
|
||||
CREATE_TASK "Approve reorder PO " + po.po_number + " for " + item.name, "high", "purchasing"
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
IF reorders_created > 0 THEN
|
||||
notification = "Created " + reorders_created + " automatic reorder POs"
|
||||
SEND MAIL "purchasing@company.com", "Automatic Reorders", notification
|
||||
END IF
|
||||
END IF
|
||||
|
||||
IF job_name = "low_stock_alert" THEN
|
||||
items = FIND "items", "minimum_stock_level > 0"
|
||||
|
||||
low_stock_items = []
|
||||
critical_items = []
|
||||
|
||||
FOR EACH item IN items DO
|
||||
stocks = FIND "inventory_stock", "item_id = '" + item.id + "'"
|
||||
|
||||
total_on_hand = 0
|
||||
FOR EACH stock IN stocks DO
|
||||
total_on_hand = total_on_hand + stock.quantity_on_hand
|
||||
END FOR
|
||||
|
||||
IF total_on_hand < item.minimum_stock_level THEN
|
||||
stock_ratio = total_on_hand / item.minimum_stock_level
|
||||
|
||||
IF stock_ratio < 0.25 THEN
|
||||
APPEND critical_items, item.name + " (" + total_on_hand + "/" + item.minimum_stock_level + ")"
|
||||
ELSE
|
||||
APPEND low_stock_items, item.name + " (" + total_on_hand + "/" + item.minimum_stock_level + ")"
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
IF LENGTH(critical_items) > 0 OR LENGTH(low_stock_items) > 0 THEN
|
||||
alert = "INVENTORY ALERT\n"
|
||||
alert = alert + "===============\n\n"
|
||||
|
||||
IF LENGTH(critical_items) > 0 THEN
|
||||
alert = alert + "CRITICAL (Below 25%):\n"
|
||||
FOR EACH item_info IN critical_items DO
|
||||
alert = alert + "- " + item_info + "\n"
|
||||
END FOR
|
||||
alert = alert + "\n"
|
||||
END IF
|
||||
|
||||
IF LENGTH(low_stock_items) > 0 THEN
|
||||
alert = alert + "LOW STOCK:\n"
|
||||
FOR EACH item_info IN low_stock_items DO
|
||||
alert = alert + "- " + item_info + "\n"
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
SEND MAIL "inventory-manager@company.com", "Low Stock Alert", alert
|
||||
END IF
|
||||
END IF
|
||||
|
||||
IF job_name = "po_follow_up" THEN
|
||||
pos = FIND "purchase_orders", "status = 'approved'"
|
||||
|
||||
FOR EACH po IN pos DO
|
||||
days_old = DAYS_BETWEEN(po.order_date, current_time)
|
||||
|
||||
IF days_old > 7 THEN
|
||||
vendor = FIND "vendors", "id = '" + po.vendor_id + "'"
|
||||
|
||||
notification = "PO " + po.po_number + " has been approved for " + days_old + " days without receipt"
|
||||
SEND MAIL po.buyer_id, "PO Follow-up Required", notification
|
||||
|
||||
CREATE_TASK "Follow up on PO " + po.po_number + " with " + vendor.name, "medium", po.buyer_id
|
||||
END IF
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
IF job_name = "cost_analysis" THEN
|
||||
start_of_month = FORMAT NOW() AS "YYYY-MM" + "-01"
|
||||
|
||||
transactions = FIND "inventory_transactions", "created_at >= '" + start_of_month + "'"
|
||||
|
||||
total_receipts_value = 0
|
||||
total_shipments_value = 0
|
||||
total_adjustments_value = 0
|
||||
|
||||
FOR EACH trans IN transactions DO
|
||||
IF trans.transaction_type = "receipt" THEN
|
||||
total_receipts_value = total_receipts_value + trans.total_cost
|
||||
ELSE IF trans.transaction_type = "shipment" THEN
|
||||
total_shipments_value = total_shipments_value + ABS(trans.total_cost)
|
||||
ELSE IF trans.transaction_type = "adjustment" THEN
|
||||
total_adjustments_value = total_adjustments_value + ABS(trans.total_cost)
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
report = "MONTHLY INVENTORY COST ANALYSIS\n"
|
||||
report = report + "================================\n"
|
||||
report = report + "Period: " + FORMAT NOW() AS "MMMM YYYY" + "\n\n"
|
||||
report = report + "Receipts Value: $" + total_receipts_value + "\n"
|
||||
report = report + "Shipments Value: $" + total_shipments_value + "\n"
|
||||
report = report + "Adjustments Value: $" + total_adjustments_value + "\n"
|
||||
report = report + "\n"
|
||||
report = report + "Gross Margin: $" + (total_shipments_value - total_receipts_value) + "\n"
|
||||
|
||||
SEND MAIL "cfo@company.com", "Monthly Inventory Cost Report", report
|
||||
END IF
|
||||
|
||||
IF job_name = "vendor_scorecard" THEN
|
||||
vendors = FIND "vendors", "status = 'active'"
|
||||
|
||||
scorecard = "VENDOR SCORECARD - " + current_time + "\n"
|
||||
scorecard = scorecard + "====================================\n\n"
|
||||
|
||||
FOR EACH vendor IN vendors DO
|
||||
pos = FIND "purchase_orders", "vendor_id = '" + vendor.id + "' AND created_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)"
|
||||
|
||||
total_pos = 0
|
||||
on_time = 0
|
||||
total_spend = 0
|
||||
|
||||
FOR EACH po IN pos DO
|
||||
total_pos = total_pos + 1
|
||||
total_spend = total_spend + po.total_amount
|
||||
|
||||
IF po.status = "received" THEN
|
||||
IF po.received_date <= po.expected_date THEN
|
||||
on_time = on_time + 1
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
IF total_pos > 0 THEN
|
||||
on_time_rate = (on_time / total_pos) * 100
|
||||
|
||||
scorecard = scorecard + vendor.name + "\n"
|
||||
scorecard = scorecard + " Orders: " + total_pos + "\n"
|
||||
scorecard = scorecard + " Spend: $" + total_spend + "\n"
|
||||
scorecard = scorecard + " On-Time: " + on_time_rate + "%\n"
|
||||
|
||||
IF on_time_rate < 80 THEN
|
||||
scorecard = scorecard + " WARNING: Low performance\n"
|
||||
END IF
|
||||
|
||||
scorecard = scorecard + "\n"
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
SEND MAIL "purchasing@company.com", "Vendor Scorecard", scorecard
|
||||
END IF
|
||||
|
||||
IF job_name = "warehouse_capacity" THEN
|
||||
warehouses = FIND "warehouses", "is_active = true"
|
||||
|
||||
capacity_report = "WAREHOUSE CAPACITY REPORT\n"
|
||||
capacity_report = capacity_report + "========================\n\n"
|
||||
|
||||
FOR EACH warehouse IN warehouses DO
|
||||
stocks = FIND "inventory_stock", "warehouse_id = '" + warehouse.id + "'"
|
||||
|
||||
total_units = 0
|
||||
FOR EACH stock IN stocks DO
|
||||
total_units = total_units + stock.quantity_on_hand
|
||||
END FOR
|
||||
|
||||
utilization = 0
|
||||
IF warehouse.capacity_units > 0 THEN
|
||||
utilization = (total_units / warehouse.capacity_units) * 100
|
||||
END IF
|
||||
|
||||
capacity_report = capacity_report + warehouse.name + "\n"
|
||||
capacity_report = capacity_report + " Units: " + total_units + " / " + warehouse.capacity_units + "\n"
|
||||
capacity_report = capacity_report + " Utilization: " + utilization + "%\n"
|
||||
|
||||
IF utilization > 90 THEN
|
||||
capacity_report = capacity_report + " WARNING: Near capacity\n"
|
||||
CREATE_TASK "Review capacity at " + warehouse.name, "high", "warehouse-manager"
|
||||
ELSE IF utilization < 20 THEN
|
||||
capacity_report = capacity_report + " NOTE: Low utilization\n"
|
||||
END IF
|
||||
|
||||
capacity_report = capacity_report + "\n"
|
||||
END FOR
|
||||
|
||||
SEND MAIL "operations@company.com", "Warehouse Capacity Report", capacity_report
|
||||
END IF
|
||||
|
||||
IF job_name = "invoice_aging" THEN
|
||||
invoices = FIND "invoices", "balance_due > 0"
|
||||
|
||||
aging_30 = 0
|
||||
aging_60 = 0
|
||||
aging_90 = 0
|
||||
aging_over_90 = 0
|
||||
|
||||
total_30 = 0
|
||||
total_60 = 0
|
||||
total_90 = 0
|
||||
total_over_90 = 0
|
||||
|
||||
FOR EACH invoice IN invoices DO
|
||||
days_old = DAYS_BETWEEN(invoice.invoice_date, current_time)
|
||||
|
||||
IF days_old <= 30 THEN
|
||||
aging_30 = aging_30 + 1
|
||||
total_30 = total_30 + invoice.balance_due
|
||||
ELSE IF days_old <= 60 THEN
|
||||
aging_60 = aging_60 + 1
|
||||
total_60 = total_60 + invoice.balance_due
|
||||
ELSE IF days_old <= 90 THEN
|
||||
aging_90 = aging_90 + 1
|
||||
total_90 = total_90 + invoice.balance_due
|
||||
ELSE
|
||||
aging_over_90 = aging_over_90 + 1
|
||||
total_over_90 = total_over_90 + invoice.balance_due
|
||||
|
||||
customer = FIND "customers", "id = '" + invoice.customer_id + "'"
|
||||
IF customer != NULL THEN
|
||||
notification = "Invoice " + invoice.invoice_number + " is over 90 days past due. Amount: $" + invoice.balance_due
|
||||
CREATE_TASK "Collection: " + customer.name + " - " + invoice.invoice_number, "critical", "collections"
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
aging_report = "ACCOUNTS RECEIVABLE AGING\n"
|
||||
aging_report = aging_report + "=========================\n\n"
|
||||
aging_report = aging_report + "0-30 days: " + aging_30 + " invoices, $" + total_30 + "\n"
|
||||
aging_report = aging_report + "31-60 days: " + aging_60 + " invoices, $" + total_60 + "\n"
|
||||
aging_report = aging_report + "61-90 days: " + aging_90 + " invoices, $" + total_90 + "\n"
|
||||
aging_report = aging_report + "Over 90 days: " + aging_over_90 + " invoices, $" + total_over_90 + "\n"
|
||||
aging_report = aging_report + "\n"
|
||||
aging_report = aging_report + "TOTAL OUTSTANDING: $" + (total_30 + total_60 + total_90 + total_over_90) + "\n"
|
||||
|
||||
SEND MAIL "finance@company.com", "AR Aging Report", aging_report
|
||||
END IF
|
||||
|
||||
IF job_name = "setup_schedules" THEN
|
||||
SET SCHEDULE "0 6 * * *" "erp-jobs.bas" "inventory_reorder"
|
||||
SET SCHEDULE "0 8,16 * * *" "erp-jobs.bas" "low_stock_alert"
|
||||
SET SCHEDULE "0 10 * * *" "erp-jobs.bas" "po_follow_up"
|
||||
SET SCHEDULE "0 0 1 * *" "erp-jobs.bas" "cost_analysis"
|
||||
SET SCHEDULE "0 9 * * MON" "erp-jobs.bas" "vendor_scorecard"
|
||||
SET SCHEDULE "0 7 * * *" "erp-jobs.bas" "warehouse_capacity"
|
||||
SET SCHEDULE "0 11 * * *" "erp-jobs.bas" "invoice_aging"
|
||||
|
||||
TALK "All ERP schedules have been configured"
|
||||
END IF
|
||||
343
erp.gbai/erp.gbdialog/inventory-management.bas
Normal file
343
erp.gbai/erp.gbdialog/inventory-management.bas
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
PARAM action AS STRING LIKE "check_stock" DESCRIPTION "Action: receive_inventory, ship_inventory, check_stock, transfer_stock, cycle_count"
|
||||
PARAM item_data AS OBJECT LIKE "{po_number: 'PO-123'}" DESCRIPTION "Data object with action-specific parameters"
|
||||
|
||||
DESCRIPTION "Manage inventory operations - receive, ship, check stock, transfer between warehouses, and cycle counts"
|
||||
|
||||
user_id = GET "session.user_id"
|
||||
warehouse_id = GET "session.warehouse_id"
|
||||
|
||||
IF action = "receive_inventory" THEN
|
||||
po_number = item_data.po_number
|
||||
po = FIND "purchase_orders", "po_number = '" + po_number + "'"
|
||||
|
||||
IF NOT po THEN
|
||||
TALK "Purchase order not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
IF po.status = "received" THEN
|
||||
TALK "This PO has already been received."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
po_lines = FIND "purchase_order_lines", "po_id = '" + po.id + "'"
|
||||
|
||||
FOR EACH line IN po_lines
|
||||
item = FIND "items", "id = '" + line.item_id + "'"
|
||||
|
||||
TALK "Receiving " + item.name + " - Ordered: " + line.quantity_ordered
|
||||
TALK "Enter quantity received:"
|
||||
HEAR qty_received AS INTEGER
|
||||
|
||||
stock = FIND "inventory_stock", "item_id = '" + item.id + "' AND warehouse_id = '" + warehouse_id + "'"
|
||||
|
||||
IF NOT stock THEN
|
||||
WITH newStock
|
||||
id = FORMAT(GUID())
|
||||
item_id = item.id
|
||||
warehouse_id = warehouse_id
|
||||
quantity_on_hand = qty_received
|
||||
last_movement_date = NOW()
|
||||
END WITH
|
||||
SAVE "inventory_stock", newStock
|
||||
ELSE
|
||||
new_qty = stock.quantity_on_hand + qty_received
|
||||
UPDATE "inventory_stock" SET quantity_on_hand = new_qty, last_movement_date = NOW() WHERE id = stock.id
|
||||
END IF
|
||||
|
||||
WITH transaction
|
||||
id = FORMAT(GUID())
|
||||
transaction_type = "receipt"
|
||||
transaction_number = "REC-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
item_id = item.id
|
||||
warehouse_id = warehouse_id
|
||||
quantity = qty_received
|
||||
unit_cost = line.unit_price
|
||||
total_cost = qty_received * line.unit_price
|
||||
reference_type = "purchase_order"
|
||||
reference_id = po.id
|
||||
created_by = user_id
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "inventory_transactions", transaction
|
||||
UPDATE "purchase_order_lines" SET quantity_received = line.quantity_received + qty_received WHERE id = line.id
|
||||
UPDATE "items" SET last_cost = line.unit_price WHERE id = item.id
|
||||
NEXT
|
||||
|
||||
UPDATE "purchase_orders" SET status = "received" WHERE id = po.id
|
||||
|
||||
TALK "Purchase order " + po_number + " received."
|
||||
SEND EMAIL po.buyer_id, "PO Received", "PO " + po_number + " received at warehouse " + warehouse_id
|
||||
|
||||
RETURN po_number
|
||||
END IF
|
||||
|
||||
IF action = "ship_inventory" THEN
|
||||
so_number = item_data.so_number
|
||||
so = FIND "sales_orders", "order_number = '" + so_number + "'"
|
||||
|
||||
IF NOT so THEN
|
||||
TALK "Sales order not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
so_lines = FIND "sales_order_lines", "order_id = '" + so.id + "'"
|
||||
can_ship = true
|
||||
|
||||
FOR EACH line IN so_lines
|
||||
item = FIND "items", "id = '" + line.item_id + "'"
|
||||
stock = FIND "inventory_stock", "item_id = '" + item.id + "' AND warehouse_id = '" + warehouse_id + "'"
|
||||
|
||||
IF NOT stock OR stock.quantity_available < line.quantity_ordered THEN
|
||||
TALK "Insufficient stock for " + item.name
|
||||
can_ship = false
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
IF NOT can_ship THEN
|
||||
TALK "Cannot ship order due to insufficient inventory."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
shipment_number = "SHIP-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
|
||||
FOR EACH line IN so_lines
|
||||
item = FIND "items", "id = '" + line.item_id + "'"
|
||||
stock = FIND "inventory_stock", "item_id = '" + item.id + "' AND warehouse_id = '" + warehouse_id + "'"
|
||||
|
||||
new_qty = stock.quantity_on_hand - line.quantity_ordered
|
||||
UPDATE "inventory_stock" SET quantity_on_hand = new_qty, last_movement_date = NOW() WHERE id = stock.id
|
||||
|
||||
WITH transaction
|
||||
id = FORMAT(GUID())
|
||||
transaction_type = "shipment"
|
||||
transaction_number = shipment_number
|
||||
item_id = item.id
|
||||
warehouse_id = warehouse_id
|
||||
quantity = 0 - line.quantity_ordered
|
||||
unit_cost = item.average_cost
|
||||
total_cost = line.quantity_ordered * item.average_cost
|
||||
reference_type = "sales_order"
|
||||
reference_id = so.id
|
||||
created_by = user_id
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "inventory_transactions", transaction
|
||||
UPDATE "sales_order_lines" SET quantity_shipped = line.quantity_ordered, cost_of_goods_sold = transaction.total_cost WHERE id = line.id
|
||||
NEXT
|
||||
|
||||
UPDATE "sales_orders" SET status = "shipped" WHERE id = so.id
|
||||
|
||||
TALK "Order " + so_number + " shipped. Tracking: " + shipment_number
|
||||
|
||||
customer = FIND "customers", "id = '" + so.customer_id + "'"
|
||||
IF customer AND customer.email THEN
|
||||
SEND EMAIL customer.email, "Order Shipped", "Your order " + so_number + " has been shipped. Tracking: " + shipment_number
|
||||
END IF
|
||||
|
||||
RETURN shipment_number
|
||||
END IF
|
||||
|
||||
IF action = "check_stock" THEN
|
||||
item_search = item_data.item_search
|
||||
items = FIND "items", "name LIKE '%" + item_search + "%' OR item_code = '" + item_search + "'"
|
||||
|
||||
IF NOT items THEN
|
||||
TALK "No items found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
FOR EACH item IN items
|
||||
TALK "Item: " + item.name + " (" + item.item_code + ")"
|
||||
|
||||
stocks = FIND "inventory_stock", "item_id = '" + item.id + "'"
|
||||
|
||||
total_on_hand = 0
|
||||
total_available = 0
|
||||
|
||||
FOR EACH stock IN stocks
|
||||
warehouse = FIND "warehouses", "id = '" + stock.warehouse_id + "'"
|
||||
TALK " " + warehouse.name + ": " + stock.quantity_on_hand + " on hand"
|
||||
total_on_hand = total_on_hand + stock.quantity_on_hand
|
||||
total_available = total_available + stock.quantity_available
|
||||
NEXT
|
||||
|
||||
TALK " TOTAL: " + total_on_hand + " on hand, " + total_available + " available"
|
||||
|
||||
IF total_available < item.minimum_stock_level THEN
|
||||
TALK " WARNING: Below minimum stock level (" + item.minimum_stock_level + ")"
|
||||
IF item.reorder_point > 0 AND total_available <= item.reorder_point THEN
|
||||
TALK " REORDER NEEDED! Qty: " + item.reorder_quantity
|
||||
CREATE_TASK "Reorder " + item.name, "high", user_id
|
||||
END IF
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
RETURN items
|
||||
END IF
|
||||
|
||||
IF action = "transfer_stock" THEN
|
||||
TALK "Enter item code:"
|
||||
HEAR item_code AS STRING
|
||||
|
||||
item = FIND "items", "item_code = '" + item_code + "'"
|
||||
|
||||
IF NOT item THEN
|
||||
TALK "Item not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
TALK "From warehouse code:"
|
||||
HEAR from_warehouse_code AS STRING
|
||||
|
||||
from_warehouse = FIND "warehouses", "code = '" + from_warehouse_code + "'"
|
||||
|
||||
IF NOT from_warehouse THEN
|
||||
TALK "Source warehouse not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
from_stock = FIND "inventory_stock", "item_id = '" + item.id + "' AND warehouse_id = '" + from_warehouse.id + "'"
|
||||
|
||||
IF NOT from_stock THEN
|
||||
TALK "No stock in source warehouse."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
TALK "Available: " + from_stock.quantity_available
|
||||
TALK "Transfer quantity:"
|
||||
HEAR transfer_qty AS INTEGER
|
||||
|
||||
IF transfer_qty > from_stock.quantity_available THEN
|
||||
TALK "Insufficient available quantity."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
TALK "To warehouse code:"
|
||||
HEAR to_warehouse_code AS STRING
|
||||
|
||||
to_warehouse = FIND "warehouses", "code = '" + to_warehouse_code + "'"
|
||||
|
||||
IF NOT to_warehouse THEN
|
||||
TALK "Destination warehouse not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
transfer_number = "TRAN-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
|
||||
new_from_qty = from_stock.quantity_on_hand - transfer_qty
|
||||
UPDATE "inventory_stock" SET quantity_on_hand = new_from_qty, last_movement_date = NOW() WHERE id = from_stock.id
|
||||
|
||||
WITH from_transaction
|
||||
id = FORMAT(GUID())
|
||||
transaction_type = "transfer_out"
|
||||
transaction_number = transfer_number
|
||||
item_id = item.id
|
||||
warehouse_id = from_warehouse.id
|
||||
quantity = 0 - transfer_qty
|
||||
unit_cost = item.average_cost
|
||||
created_by = user_id
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "inventory_transactions", from_transaction
|
||||
|
||||
to_stock = FIND "inventory_stock", "item_id = '" + item.id + "' AND warehouse_id = '" + to_warehouse.id + "'"
|
||||
|
||||
IF NOT to_stock THEN
|
||||
WITH newToStock
|
||||
id = FORMAT(GUID())
|
||||
item_id = item.id
|
||||
warehouse_id = to_warehouse.id
|
||||
quantity_on_hand = transfer_qty
|
||||
last_movement_date = NOW()
|
||||
END WITH
|
||||
SAVE "inventory_stock", newToStock
|
||||
ELSE
|
||||
new_to_qty = to_stock.quantity_on_hand + transfer_qty
|
||||
UPDATE "inventory_stock" SET quantity_on_hand = new_to_qty, last_movement_date = NOW() WHERE id = to_stock.id
|
||||
END IF
|
||||
|
||||
WITH to_transaction
|
||||
id = FORMAT(GUID())
|
||||
transaction_type = "transfer_in"
|
||||
transaction_number = transfer_number
|
||||
item_id = item.id
|
||||
warehouse_id = to_warehouse.id
|
||||
quantity = transfer_qty
|
||||
unit_cost = item.average_cost
|
||||
created_by = user_id
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "inventory_transactions", to_transaction
|
||||
|
||||
TALK "Transfer " + transfer_number + " completed: " + transfer_qty + " units from " + from_warehouse.name + " to " + to_warehouse.name
|
||||
|
||||
RETURN transfer_number
|
||||
END IF
|
||||
|
||||
IF action = "cycle_count" THEN
|
||||
TALK "Enter warehouse code:"
|
||||
HEAR warehouse_code AS STRING
|
||||
|
||||
warehouse = FIND "warehouses", "code = '" + warehouse_code + "'"
|
||||
|
||||
IF NOT warehouse THEN
|
||||
TALK "Warehouse not found."
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
stocks = FIND "inventory_stock", "warehouse_id = '" + warehouse.id + "'"
|
||||
|
||||
count_number = "COUNT-" + FORMAT(NOW(), "YYYYMMDD") + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
adjustments = 0
|
||||
|
||||
FOR EACH stock IN stocks
|
||||
item = FIND "items", "id = '" + stock.item_id + "'"
|
||||
|
||||
TALK "Item: " + item.name + " (" + item.item_code + ")"
|
||||
TALK "System quantity: " + stock.quantity_on_hand
|
||||
TALK "Enter physical count:"
|
||||
HEAR physical_count AS INTEGER
|
||||
|
||||
IF physical_count <> stock.quantity_on_hand THEN
|
||||
variance = physical_count - stock.quantity_on_hand
|
||||
|
||||
WITH adjustment
|
||||
id = FORMAT(GUID())
|
||||
transaction_type = "adjustment"
|
||||
transaction_number = count_number
|
||||
item_id = item.id
|
||||
warehouse_id = warehouse.id
|
||||
quantity = variance
|
||||
notes = "Cycle count adjustment"
|
||||
created_by = user_id
|
||||
created_at = NOW()
|
||||
END WITH
|
||||
|
||||
SAVE "inventory_transactions", adjustment
|
||||
|
||||
UPDATE "inventory_stock" SET quantity_on_hand = physical_count, last_counted_date = NOW(), last_movement_date = NOW() WHERE id = stock.id
|
||||
|
||||
adjustments = adjustments + 1
|
||||
TALK " Adjusted by " + variance + " units"
|
||||
ELSE
|
||||
UPDATE "inventory_stock" SET last_counted_date = NOW() WHERE id = stock.id
|
||||
TALK " Count confirmed"
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
TALK "Cycle count " + count_number + " completed with " + adjustments + " adjustments"
|
||||
|
||||
IF adjustments > 0 THEN
|
||||
SEND EMAIL "inventory-manager@company.com", "Cycle Count Results", "Cycle count " + count_number + " at " + warehouse.name + " with " + adjustments + " adjustments"
|
||||
END IF
|
||||
|
||||
RETURN count_number
|
||||
END IF
|
||||
|
||||
TALK "Unknown action: " + action
|
||||
RETURN NULL
|
||||
341
erp.gbai/erp.gbdialog/purchasing.bas
Normal file
341
erp.gbai/erp.gbdialog/purchasing.bas
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
PARAM action AS STRING
|
||||
PARAM purchase_data AS OBJECT
|
||||
|
||||
user_id = GET "session.user_id"
|
||||
current_time = FORMAT NOW() AS "YYYY-MM-DD HH:mm:ss"
|
||||
|
||||
IF action = "create_po" THEN
|
||||
vendor_code = GET "purchase_data.vendor_code"
|
||||
|
||||
IF vendor_code = "" THEN
|
||||
TALK "Enter vendor code:"
|
||||
vendor_code = HEAR
|
||||
END IF
|
||||
|
||||
vendor = FIND "vendors", "vendor_code = '" + vendor_code + "'"
|
||||
|
||||
IF vendor = NULL THEN
|
||||
TALK "Vendor not found."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
po_number = "PO-" + FORMAT NOW() AS "YYYYMMDD" + "-" + FORMAT RANDOM(1000, 9999)
|
||||
|
||||
po = CREATE OBJECT
|
||||
SET po.id = FORMAT GUID()
|
||||
SET po.po_number = po_number
|
||||
SET po.vendor_id = vendor.id
|
||||
SET po.order_date = current_time
|
||||
SET po.status = "draft"
|
||||
SET po.buyer_id = user_id
|
||||
SET po.created_by = user_id
|
||||
SET po.created_at = current_time
|
||||
SET po.subtotal = 0
|
||||
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_orders", FORMAT po AS JSON
|
||||
|
||||
SET "session.po_id" = po.id
|
||||
REMEMBER "po_" + po.id = po
|
||||
|
||||
TALK "Purchase Order " + po_number + " created for " + vendor.name
|
||||
|
||||
adding_items = true
|
||||
line_number = 1
|
||||
total = 0
|
||||
|
||||
WHILE adding_items = true DO
|
||||
TALK "Enter item code (or 'done' to finish):"
|
||||
HEAR item_code AS "done", *
|
||||
|
||||
IF item_code = "done" THEN
|
||||
adding_items = false
|
||||
ELSE
|
||||
item = FIND "items", "item_code = '" + item_code + "'"
|
||||
|
||||
IF item = NULL THEN
|
||||
TALK "Item not found. Try again."
|
||||
ELSE
|
||||
TALK "Quantity to order:"
|
||||
HEAR quantity AS INTEGER
|
||||
|
||||
TALK "Unit price (or press Enter for last cost: " + item.last_cost + "):"
|
||||
HEAR unit_price AS MONEY DEFAULT item.last_cost
|
||||
|
||||
line = CREATE OBJECT
|
||||
SET line.id = FORMAT GUID()
|
||||
SET line.po_id = po.id
|
||||
SET line.line_number = line_number
|
||||
SET line.item_id = item.id
|
||||
SET line.description = item.name
|
||||
SET line.quantity_ordered = quantity
|
||||
SET line.unit_price = unit_price
|
||||
SET line.line_total = quantity * unit_price
|
||||
SET line.created_at = current_time
|
||||
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_order_lines", FORMAT line AS JSON
|
||||
|
||||
total = total + line.line_total
|
||||
line_number = line_number + 1
|
||||
|
||||
TALK "Added: " + item.name + " x " + quantity + " @ $" + unit_price
|
||||
END IF
|
||||
END IF
|
||||
END WHILE
|
||||
|
||||
po.subtotal = total
|
||||
po.total_amount = total
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_orders", FORMAT po AS JSON
|
||||
|
||||
TALK "Purchase Order " + po_number + " total: $" + total
|
||||
|
||||
END IF
|
||||
|
||||
IF action = "approve_po" THEN
|
||||
po_number = GET "purchase_data.po_number"
|
||||
|
||||
IF po_number = "" THEN
|
||||
TALK "Enter PO number to approve:"
|
||||
HEAR po_number
|
||||
END IF
|
||||
|
||||
po = FIND "purchase_orders", "po_number = '" + po_number + "'"
|
||||
|
||||
IF po = NULL THEN
|
||||
TALK "Purchase order not found."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
IF po.status != "draft" THEN
|
||||
TALK "PO status is " + po.status + ". Can only approve draft POs."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
po_lines = FIND "purchase_order_lines", "po_id = '" + po.id + "'"
|
||||
|
||||
TALK "PO Summary:"
|
||||
TALK "Vendor: " + po.vendor_id
|
||||
TALK "Total: $" + po.total_amount
|
||||
TALK "Items:"
|
||||
|
||||
FOR EACH line IN po_lines DO
|
||||
TALK " - " + line.description + " x " + line.quantity_ordered + " @ $" + line.unit_price
|
||||
END FOR
|
||||
|
||||
TALK "Approve this PO? (yes/no)"
|
||||
HEAR approval AS "yes", "no"
|
||||
|
||||
IF approval = "yes" THEN
|
||||
po.status = "approved"
|
||||
po.approved_by = user_id
|
||||
po.approved_date = current_time
|
||||
SAVE_FROM_UNSTRUCTURED "purchase_orders", FORMAT po AS JSON
|
||||
|
||||
vendor = FIND "vendors", "id = '" + po.vendor_id + "'"
|
||||
|
||||
IF vendor.email != "" THEN
|
||||
message = "Purchase Order " + po_number + " has been approved. Total: $" + po.total_amount
|
||||
SEND MAIL vendor.email, "PO Approved: " + po_number, message
|
||||
END IF
|
||||
|
||||
TALK "PO " + po_number + " approved successfully."
|
||||
|
||||
CREATE_TASK "Process PO " + po_number, "high", user_id
|
||||
|
||||
ELSE
|
||||
TALK "PO not approved."
|
||||
END IF
|
||||
|
||||
END IF
|
||||
|
||||
IF action = "vendor_performance" THEN
|
||||
vendor_code = GET "purchase_data.vendor_code"
|
||||
|
||||
IF vendor_code = "" THEN
|
||||
TALK "Enter vendor code:"
|
||||
HEAR vendor_code
|
||||
END IF
|
||||
|
||||
vendor = FIND "vendors", "vendor_code = '" + vendor_code + "'"
|
||||
|
||||
IF vendor = NULL THEN
|
||||
TALK "Vendor not found."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
pos = FIND "purchase_orders", "vendor_id = '" + vendor.id + "'"
|
||||
|
||||
total_pos = 0
|
||||
on_time = 0
|
||||
late = 0
|
||||
total_spend = 0
|
||||
|
||||
FOR EACH po IN pos DO
|
||||
total_pos = total_pos + 1
|
||||
total_spend = total_spend + po.total_amount
|
||||
|
||||
IF po.status = "received" THEN
|
||||
IF po.received_date <= po.expected_date THEN
|
||||
on_time = on_time + 1
|
||||
ELSE
|
||||
late = late + 1
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
on_time_percentage = 0
|
||||
IF total_pos > 0 THEN
|
||||
on_time_percentage = (on_time / total_pos) * 100
|
||||
END IF
|
||||
|
||||
TALK "VENDOR PERFORMANCE: " + vendor.name
|
||||
TALK "================================"
|
||||
TALK "Total Purchase Orders: " + total_pos
|
||||
TALK "Total Spend: $" + total_spend
|
||||
TALK "On-Time Delivery: " + on_time_percentage + "%"
|
||||
TALK "Late Deliveries: " + late
|
||||
|
||||
IF on_time_percentage < 80 THEN
|
||||
TALK "WARNING: Low on-time delivery rate"
|
||||
CREATE_TASK "Review vendor " + vendor.name + " performance", "medium", user_id
|
||||
END IF
|
||||
|
||||
END IF
|
||||
|
||||
IF action = "reorder_check" THEN
|
||||
items = FIND "items", "is_purchasable = true"
|
||||
|
||||
reorder_needed = 0
|
||||
|
||||
FOR EACH item IN items DO
|
||||
IF item.reorder_point > 0 THEN
|
||||
stocks = FIND "inventory_stock", "item_id = '" + item.id + "'"
|
||||
|
||||
total_available = 0
|
||||
FOR EACH stock IN stocks DO
|
||||
total_available = total_available + stock.quantity_available
|
||||
END FOR
|
||||
|
||||
IF total_available <= item.reorder_point THEN
|
||||
reorder_needed = reorder_needed + 1
|
||||
|
||||
TALK "REORDER: " + item.name
|
||||
TALK " Current stock: " + total_available
|
||||
TALK " Reorder point: " + item.reorder_point
|
||||
TALK " Suggested qty: " + item.reorder_quantity
|
||||
|
||||
preferred_vendor = FIND "vendor_items", "item_id = '" + item.id + "' AND is_preferred = true"
|
||||
|
||||
IF preferred_vendor != NULL THEN
|
||||
vendor = FIND "vendors", "id = '" + preferred_vendor.vendor_id + "'"
|
||||
TALK " Preferred vendor: " + vendor.name
|
||||
END IF
|
||||
|
||||
CREATE_TASK "Reorder " + item.name + " (qty: " + item.reorder_quantity + ")", "high", user_id
|
||||
END IF
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
IF reorder_needed = 0 THEN
|
||||
TALK "No items need reordering."
|
||||
ELSE
|
||||
TALK "Total items needing reorder: " + reorder_needed
|
||||
END IF
|
||||
|
||||
END IF
|
||||
|
||||
IF action = "requisition" THEN
|
||||
req_number = "REQ-" + FORMAT NOW() AS "YYYYMMDD" + "-" + FORMAT RANDOM(1000, 9999)
|
||||
|
||||
TALK "Creating requisition " + req_number
|
||||
|
||||
req = CREATE OBJECT
|
||||
SET req.id = FORMAT GUID()
|
||||
SET req.req_number = req_number
|
||||
SET req.requester = user_id
|
||||
SET req.status = "pending"
|
||||
SET req.created_at = current_time
|
||||
SET req.items = []
|
||||
|
||||
adding = true
|
||||
|
||||
WHILE adding = true DO
|
||||
TALK "Enter item description (or 'done'):"
|
||||
HEAR item_desc AS "done", *
|
||||
|
||||
IF item_desc = "done" THEN
|
||||
adding = false
|
||||
ELSE
|
||||
TALK "Quantity needed:"
|
||||
HEAR quantity AS INTEGER
|
||||
|
||||
TALK "Reason/Project:"
|
||||
HEAR reason
|
||||
|
||||
req_item = CREATE OBJECT
|
||||
SET req_item.description = item_desc
|
||||
SET req_item.quantity = quantity
|
||||
SET req_item.reason = reason
|
||||
|
||||
APPEND req.items, req_item
|
||||
|
||||
TALK "Added to requisition."
|
||||
END IF
|
||||
END WHILE
|
||||
|
||||
SAVE_FROM_UNSTRUCTURED "requisitions", FORMAT req AS JSON
|
||||
|
||||
TALK "Requisition " + req_number + " created with " + LENGTH(req.items) + " items."
|
||||
|
||||
notification = "New requisition " + req_number + " from " + user_id + " needs approval"
|
||||
SEND MAIL "purchasing@company.com", "New Requisition", notification
|
||||
|
||||
CREATE_TASK "Review requisition " + req_number, "medium", "purchasing"
|
||||
|
||||
END IF
|
||||
|
||||
IF action = "price_comparison" THEN
|
||||
item_code = GET "purchase_data.item_code"
|
||||
|
||||
IF item_code = "" THEN
|
||||
TALK "Enter item code:"
|
||||
HEAR item_code
|
||||
END IF
|
||||
|
||||
item = FIND "items", "item_code = '" + item_code + "'"
|
||||
|
||||
IF item = NULL THEN
|
||||
TALK "Item not found."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
vendor_items = FIND "vendor_items", "item_id = '" + item.id + "'"
|
||||
|
||||
IF vendor_items = NULL THEN
|
||||
TALK "No vendor pricing found for this item."
|
||||
EXIT
|
||||
END IF
|
||||
|
||||
TALK "PRICE COMPARISON: " + item.name
|
||||
TALK "================================"
|
||||
|
||||
best_price = 999999
|
||||
best_vendor = ""
|
||||
|
||||
FOR EACH vi IN vendor_items DO
|
||||
vendor = FIND "vendors", "id = '" + vi.vendor_id + "'"
|
||||
|
||||
TALK vendor.name + ":"
|
||||
TALK " Unit price: $" + vi.unit_price
|
||||
TALK " Min order: " + vi.min_order_qty
|
||||
TALK " Lead time: " + vi.lead_time_days + " days"
|
||||
|
||||
IF vi.unit_price < best_price THEN
|
||||
best_price = vi.unit_price
|
||||
best_vendor = vendor.name
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
TALK ""
|
||||
TALK "Best price: $" + best_price + " from " + best_vendor
|
||||
|
||||
END IF
|
||||
509
erp.gbai/erp.gbdialog/tables.bas
Normal file
509
erp.gbai/erp.gbdialog/tables.bas
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
' ERP Database Tables Definition
|
||||
' This file defines all ERP tables using the TABLE keyword
|
||||
' Tables cover inventory, purchasing, manufacturing, finance, and HR modules
|
||||
|
||||
' === INVENTORY MANAGEMENT ===
|
||||
|
||||
' Items/Products master table
|
||||
TABLE items
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
item_code VARCHAR(50) UNIQUE NOT NULL
|
||||
barcode VARCHAR(50) UNIQUE
|
||||
name VARCHAR(255) NOT NULL
|
||||
description TEXT
|
||||
category VARCHAR(100)
|
||||
subcategory VARCHAR(100)
|
||||
unit_of_measure VARCHAR(20) DEFAULT 'EACH'
|
||||
weight DECIMAL(10,3)
|
||||
dimensions_length DECIMAL(10,2)
|
||||
dimensions_width DECIMAL(10,2)
|
||||
dimensions_height DECIMAL(10,2)
|
||||
minimum_stock_level INTEGER DEFAULT 0
|
||||
reorder_point INTEGER
|
||||
reorder_quantity INTEGER
|
||||
lead_time_days INTEGER DEFAULT 0
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
is_purchasable BOOLEAN DEFAULT TRUE
|
||||
is_saleable BOOLEAN DEFAULT TRUE
|
||||
is_manufactured BOOLEAN DEFAULT FALSE
|
||||
standard_cost DECIMAL(15,4)
|
||||
last_cost DECIMAL(15,4)
|
||||
average_cost DECIMAL(15,4)
|
||||
selling_price DECIMAL(15,4)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Warehouses table
|
||||
TABLE warehouses
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
code VARCHAR(20) UNIQUE NOT NULL
|
||||
name VARCHAR(100) NOT NULL
|
||||
type VARCHAR(50) DEFAULT 'standard'
|
||||
address TEXT
|
||||
city VARCHAR(100)
|
||||
state VARCHAR(50)
|
||||
country VARCHAR(50)
|
||||
postal_code VARCHAR(20)
|
||||
contact_person VARCHAR(100)
|
||||
contact_phone VARCHAR(50)
|
||||
contact_email VARCHAR(100)
|
||||
capacity_units INTEGER
|
||||
current_occupancy INTEGER DEFAULT 0
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Inventory stock levels
|
||||
TABLE inventory_stock
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
item_id UUID REFERENCES items(id)
|
||||
warehouse_id UUID REFERENCES warehouses(id)
|
||||
location_code VARCHAR(50)
|
||||
quantity_on_hand DECIMAL(15,3) DEFAULT 0
|
||||
quantity_reserved DECIMAL(15,3) DEFAULT 0
|
||||
quantity_available DECIMAL(15,3) GENERATED ALWAYS AS (quantity_on_hand - quantity_reserved) STORED
|
||||
quantity_on_order DECIMAL(15,3) DEFAULT 0
|
||||
last_counted_date DATE
|
||||
last_movement_date TIMESTAMP
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(item_id, warehouse_id, location_code)
|
||||
END TABLE
|
||||
|
||||
' Inventory transactions
|
||||
TABLE inventory_transactions
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
transaction_type VARCHAR(50) NOT NULL
|
||||
transaction_number VARCHAR(50) UNIQUE
|
||||
item_id UUID REFERENCES items(id)
|
||||
warehouse_id UUID REFERENCES warehouses(id)
|
||||
location_code VARCHAR(50)
|
||||
quantity DECIMAL(15,3) NOT NULL
|
||||
unit_cost DECIMAL(15,4)
|
||||
total_cost DECIMAL(15,2)
|
||||
reference_type VARCHAR(50)
|
||||
reference_id UUID
|
||||
notes TEXT
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' === PURCHASING MODULE ===
|
||||
|
||||
' Vendors/Suppliers table
|
||||
TABLE vendors
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
vendor_code VARCHAR(50) UNIQUE NOT NULL
|
||||
name VARCHAR(255) NOT NULL
|
||||
legal_name VARCHAR(255)
|
||||
tax_id VARCHAR(50)
|
||||
vendor_type VARCHAR(50)
|
||||
status VARCHAR(20) DEFAULT 'active'
|
||||
rating INTEGER CHECK (rating >= 1 AND rating <= 5)
|
||||
payment_terms VARCHAR(50)
|
||||
credit_limit DECIMAL(15,2)
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
address TEXT
|
||||
city VARCHAR(100)
|
||||
state VARCHAR(50)
|
||||
country VARCHAR(50)
|
||||
postal_code VARCHAR(20)
|
||||
phone VARCHAR(50)
|
||||
email VARCHAR(100)
|
||||
website VARCHAR(255)
|
||||
contact_person VARCHAR(100)
|
||||
bank_account_number VARCHAR(50)
|
||||
bank_name VARCHAR(100)
|
||||
notes TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Purchase orders
|
||||
TABLE purchase_orders
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
po_number VARCHAR(50) UNIQUE NOT NULL
|
||||
vendor_id UUID REFERENCES vendors(id)
|
||||
order_date DATE NOT NULL
|
||||
expected_date DATE
|
||||
status VARCHAR(50) DEFAULT 'draft'
|
||||
buyer_id VARCHAR(100)
|
||||
ship_to_warehouse_id UUID REFERENCES warehouses(id)
|
||||
shipping_method VARCHAR(50)
|
||||
payment_terms VARCHAR(50)
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
exchange_rate DECIMAL(10,6) DEFAULT 1.0
|
||||
subtotal DECIMAL(15,2)
|
||||
tax_amount DECIMAL(15,2)
|
||||
shipping_cost DECIMAL(15,2)
|
||||
total_amount DECIMAL(15,2)
|
||||
notes TEXT
|
||||
approved_by VARCHAR(100)
|
||||
approved_date TIMESTAMP
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Purchase order lines
|
||||
TABLE purchase_order_lines
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
po_id UUID REFERENCES purchase_orders(id) ON DELETE CASCADE
|
||||
line_number INTEGER NOT NULL
|
||||
item_id UUID REFERENCES items(id)
|
||||
description TEXT
|
||||
quantity_ordered DECIMAL(15,3) NOT NULL
|
||||
quantity_received DECIMAL(15,3) DEFAULT 0
|
||||
quantity_remaining DECIMAL(15,3) GENERATED ALWAYS AS (quantity_ordered - quantity_received) STORED
|
||||
unit_price DECIMAL(15,4) NOT NULL
|
||||
discount_percent DECIMAL(5,2) DEFAULT 0
|
||||
tax_rate DECIMAL(5,2) DEFAULT 0
|
||||
line_total DECIMAL(15,2) GENERATED ALWAYS AS (quantity_ordered * unit_price * (1 - discount_percent/100)) STORED
|
||||
expected_date DATE
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(po_id, line_number)
|
||||
END TABLE
|
||||
|
||||
' === SALES MODULE ===
|
||||
|
||||
' Customers table
|
||||
TABLE customers
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
customer_code VARCHAR(50) UNIQUE NOT NULL
|
||||
name VARCHAR(255) NOT NULL
|
||||
legal_name VARCHAR(255)
|
||||
tax_id VARCHAR(50)
|
||||
customer_type VARCHAR(50)
|
||||
status VARCHAR(20) DEFAULT 'active'
|
||||
credit_rating VARCHAR(10)
|
||||
credit_limit DECIMAL(15,2)
|
||||
payment_terms VARCHAR(50)
|
||||
discount_percent DECIMAL(5,2) DEFAULT 0
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
billing_address TEXT
|
||||
shipping_address TEXT
|
||||
city VARCHAR(100)
|
||||
state VARCHAR(50)
|
||||
country VARCHAR(50)
|
||||
postal_code VARCHAR(20)
|
||||
phone VARCHAR(50)
|
||||
email VARCHAR(100)
|
||||
website VARCHAR(255)
|
||||
sales_person_id VARCHAR(100)
|
||||
notes TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Sales orders
|
||||
TABLE sales_orders
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
order_number VARCHAR(50) UNIQUE NOT NULL
|
||||
customer_id UUID REFERENCES customers(id)
|
||||
order_date DATE NOT NULL
|
||||
required_date DATE
|
||||
promised_date DATE
|
||||
status VARCHAR(50) DEFAULT 'draft'
|
||||
sales_person_id VARCHAR(100)
|
||||
ship_from_warehouse_id UUID REFERENCES warehouses(id)
|
||||
shipping_method VARCHAR(50)
|
||||
payment_terms VARCHAR(50)
|
||||
payment_method VARCHAR(50)
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
exchange_rate DECIMAL(10,6) DEFAULT 1.0
|
||||
subtotal DECIMAL(15,2)
|
||||
discount_amount DECIMAL(15,2) DEFAULT 0
|
||||
tax_amount DECIMAL(15,2)
|
||||
shipping_cost DECIMAL(15,2)
|
||||
total_amount DECIMAL(15,2)
|
||||
notes TEXT
|
||||
approved_by VARCHAR(100)
|
||||
approved_date TIMESTAMP
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Sales order lines
|
||||
TABLE sales_order_lines
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
order_id UUID REFERENCES sales_orders(id) ON DELETE CASCADE
|
||||
line_number INTEGER NOT NULL
|
||||
item_id UUID REFERENCES items(id)
|
||||
description TEXT
|
||||
quantity_ordered DECIMAL(15,3) NOT NULL
|
||||
quantity_shipped DECIMAL(15,3) DEFAULT 0
|
||||
quantity_invoiced DECIMAL(15,3) DEFAULT 0
|
||||
unit_price DECIMAL(15,4) NOT NULL
|
||||
discount_percent DECIMAL(5,2) DEFAULT 0
|
||||
tax_rate DECIMAL(5,2) DEFAULT 0
|
||||
line_total DECIMAL(15,2) GENERATED ALWAYS AS (quantity_ordered * unit_price * (1 - discount_percent/100)) STORED
|
||||
cost_of_goods_sold DECIMAL(15,2)
|
||||
margin DECIMAL(15,2) GENERATED ALWAYS AS (line_total - cost_of_goods_sold) STORED
|
||||
warehouse_id UUID REFERENCES warehouses(id)
|
||||
promised_date DATE
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(order_id, line_number)
|
||||
END TABLE
|
||||
|
||||
' === MANUFACTURING MODULE ===
|
||||
|
||||
' Bill of Materials (BOM) header
|
||||
TABLE bill_of_materials
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
bom_number VARCHAR(50) UNIQUE NOT NULL
|
||||
item_id UUID REFERENCES items(id)
|
||||
revision VARCHAR(20) DEFAULT 'A'
|
||||
description TEXT
|
||||
quantity_per_assembly DECIMAL(15,3) DEFAULT 1
|
||||
unit_of_measure VARCHAR(20)
|
||||
status VARCHAR(20) DEFAULT 'active'
|
||||
effective_date DATE
|
||||
expiration_date DATE
|
||||
total_cost DECIMAL(15,4)
|
||||
labor_cost DECIMAL(15,4)
|
||||
overhead_cost DECIMAL(15,4)
|
||||
created_by VARCHAR(100)
|
||||
approved_by VARCHAR(100)
|
||||
approved_date DATE
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' BOM components
|
||||
TABLE bom_components
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
bom_id UUID REFERENCES bill_of_materials(id) ON DELETE CASCADE
|
||||
component_item_id UUID REFERENCES items(id)
|
||||
line_number INTEGER NOT NULL
|
||||
quantity_required DECIMAL(15,6) NOT NULL
|
||||
unit_of_measure VARCHAR(20)
|
||||
scrap_percent DECIMAL(5,2) DEFAULT 0
|
||||
total_quantity DECIMAL(15,6) GENERATED ALWAYS AS (quantity_required * (1 + scrap_percent/100)) STORED
|
||||
cost_per_unit DECIMAL(15,4)
|
||||
total_cost DECIMAL(15,4) GENERATED ALWAYS AS (total_quantity * cost_per_unit) STORED
|
||||
notes TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(bom_id, line_number)
|
||||
END TABLE
|
||||
|
||||
' Work orders
|
||||
TABLE work_orders
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
wo_number VARCHAR(50) UNIQUE NOT NULL
|
||||
item_id UUID REFERENCES items(id)
|
||||
bom_id UUID REFERENCES bill_of_materials(id)
|
||||
quantity_to_produce DECIMAL(15,3) NOT NULL
|
||||
quantity_completed DECIMAL(15,3) DEFAULT 0
|
||||
quantity_scrapped DECIMAL(15,3) DEFAULT 0
|
||||
status VARCHAR(50) DEFAULT 'planned'
|
||||
priority VARCHAR(20) DEFAULT 'normal'
|
||||
planned_start_date TIMESTAMP
|
||||
planned_end_date TIMESTAMP
|
||||
actual_start_date TIMESTAMP
|
||||
actual_end_date TIMESTAMP
|
||||
warehouse_id UUID REFERENCES warehouses(id)
|
||||
work_center VARCHAR(50)
|
||||
labor_hours_estimated DECIMAL(10,2)
|
||||
labor_hours_actual DECIMAL(10,2)
|
||||
notes TEXT
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' === FINANCIAL MODULE ===
|
||||
|
||||
' General ledger accounts
|
||||
TABLE gl_accounts
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
account_number VARCHAR(20) UNIQUE NOT NULL
|
||||
account_name VARCHAR(100) NOT NULL
|
||||
account_type VARCHAR(50) NOT NULL
|
||||
parent_account_id UUID REFERENCES gl_accounts(id)
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
normal_balance VARCHAR(10) CHECK (normal_balance IN ('debit', 'credit'))
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
is_control_account BOOLEAN DEFAULT FALSE
|
||||
allow_manual_entry BOOLEAN DEFAULT TRUE
|
||||
description TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Journal entries header
|
||||
TABLE journal_entries
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
journal_number VARCHAR(50) UNIQUE NOT NULL
|
||||
journal_date DATE NOT NULL
|
||||
posting_date DATE NOT NULL
|
||||
period VARCHAR(20)
|
||||
journal_type VARCHAR(50)
|
||||
description TEXT
|
||||
reference_type VARCHAR(50)
|
||||
reference_number VARCHAR(50)
|
||||
status VARCHAR(20) DEFAULT 'draft'
|
||||
total_debit DECIMAL(15,2)
|
||||
total_credit DECIMAL(15,2)
|
||||
is_balanced BOOLEAN GENERATED ALWAYS AS (total_debit = total_credit) STORED
|
||||
posted_by VARCHAR(100)
|
||||
posted_at TIMESTAMP
|
||||
reversed_by_id UUID REFERENCES journal_entries(id)
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Journal entry lines
|
||||
TABLE journal_entry_lines
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
journal_entry_id UUID REFERENCES journal_entries(id) ON DELETE CASCADE
|
||||
line_number INTEGER NOT NULL
|
||||
account_id UUID REFERENCES gl_accounts(id)
|
||||
debit_amount DECIMAL(15,2) DEFAULT 0
|
||||
credit_amount DECIMAL(15,2) DEFAULT 0
|
||||
description TEXT
|
||||
dimension1 VARCHAR(50)
|
||||
dimension2 VARCHAR(50)
|
||||
dimension3 VARCHAR(50)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(journal_entry_id, line_number)
|
||||
CHECK (debit_amount = 0 OR credit_amount = 0)
|
||||
END TABLE
|
||||
|
||||
' Invoices
|
||||
TABLE invoices
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
invoice_number VARCHAR(50) UNIQUE NOT NULL
|
||||
invoice_type VARCHAR(20) DEFAULT 'standard'
|
||||
customer_id UUID REFERENCES customers(id)
|
||||
vendor_id UUID REFERENCES vendors(id)
|
||||
order_id UUID
|
||||
invoice_date DATE NOT NULL
|
||||
due_date DATE NOT NULL
|
||||
status VARCHAR(20) DEFAULT 'draft'
|
||||
currency_code VARCHAR(3) DEFAULT 'USD'
|
||||
exchange_rate DECIMAL(10,6) DEFAULT 1.0
|
||||
subtotal DECIMAL(15,2)
|
||||
discount_amount DECIMAL(15,2) DEFAULT 0
|
||||
tax_amount DECIMAL(15,2)
|
||||
total_amount DECIMAL(15,2)
|
||||
amount_paid DECIMAL(15,2) DEFAULT 0
|
||||
balance_due DECIMAL(15,2) GENERATED ALWAYS AS (total_amount - amount_paid) STORED
|
||||
payment_terms VARCHAR(50)
|
||||
notes TEXT
|
||||
created_by VARCHAR(100)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' === HUMAN RESOURCES MODULE ===
|
||||
|
||||
' Employees table
|
||||
TABLE employees
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
employee_number VARCHAR(50) UNIQUE NOT NULL
|
||||
first_name VARCHAR(100) NOT NULL
|
||||
last_name VARCHAR(100) NOT NULL
|
||||
middle_name VARCHAR(100)
|
||||
full_name VARCHAR(255) GENERATED ALWAYS AS (first_name || ' ' || COALESCE(middle_name || ' ', '') || last_name) STORED
|
||||
email VARCHAR(100) UNIQUE
|
||||
phone VARCHAR(50)
|
||||
mobile VARCHAR(50)
|
||||
address TEXT
|
||||
city VARCHAR(100)
|
||||
state VARCHAR(50)
|
||||
country VARCHAR(50)
|
||||
postal_code VARCHAR(20)
|
||||
date_of_birth DATE
|
||||
gender VARCHAR(20)
|
||||
marital_status VARCHAR(20)
|
||||
national_id VARCHAR(50)
|
||||
passport_number VARCHAR(50)
|
||||
department_id UUID
|
||||
position_title VARCHAR(100)
|
||||
manager_id UUID REFERENCES employees(id)
|
||||
hire_date DATE NOT NULL
|
||||
employment_status VARCHAR(50) DEFAULT 'active'
|
||||
employment_type VARCHAR(50) DEFAULT 'full-time'
|
||||
salary DECIMAL(15,2)
|
||||
hourly_rate DECIMAL(10,2)
|
||||
commission_percent DECIMAL(5,2)
|
||||
bank_account_number VARCHAR(50)
|
||||
bank_name VARCHAR(100)
|
||||
emergency_contact_name VARCHAR(100)
|
||||
emergency_contact_phone VARCHAR(50)
|
||||
notes TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' Payroll records
|
||||
TABLE payroll
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
payroll_number VARCHAR(50) UNIQUE NOT NULL
|
||||
employee_id UUID REFERENCES employees(id)
|
||||
pay_period_start DATE NOT NULL
|
||||
pay_period_end DATE NOT NULL
|
||||
payment_date DATE NOT NULL
|
||||
hours_worked DECIMAL(10,2)
|
||||
overtime_hours DECIMAL(10,2)
|
||||
regular_pay DECIMAL(15,2)
|
||||
overtime_pay DECIMAL(15,2)
|
||||
commission DECIMAL(15,2)
|
||||
bonus DECIMAL(15,2)
|
||||
gross_pay DECIMAL(15,2)
|
||||
tax_deductions DECIMAL(15,2)
|
||||
other_deductions DECIMAL(15,2)
|
||||
net_pay DECIMAL(15,2)
|
||||
payment_method VARCHAR(50)
|
||||
payment_reference VARCHAR(100)
|
||||
status VARCHAR(20) DEFAULT 'pending'
|
||||
approved_by VARCHAR(100)
|
||||
approved_date TIMESTAMP
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' === SYSTEM TABLES ===
|
||||
|
||||
' Audit trail
|
||||
TABLE erp_audit_log
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
table_name VARCHAR(50) NOT NULL
|
||||
record_id UUID NOT NULL
|
||||
action VARCHAR(20) NOT NULL
|
||||
changed_fields JSONB
|
||||
old_values JSONB
|
||||
new_values JSONB
|
||||
user_id VARCHAR(100)
|
||||
user_ip VARCHAR(45)
|
||||
user_agent TEXT
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
END TABLE
|
||||
|
||||
' System settings
|
||||
TABLE erp_settings
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
|
||||
module VARCHAR(50) NOT NULL
|
||||
setting_key VARCHAR(100) NOT NULL
|
||||
setting_value TEXT
|
||||
data_type VARCHAR(20)
|
||||
description TEXT
|
||||
is_encrypted BOOLEAN DEFAULT FALSE
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(module, setting_key)
|
||||
END TABLE
|
||||
|
||||
' Create indexes for performance
|
||||
CREATE INDEX idx_inventory_item_warehouse ON inventory_stock(item_id, warehouse_id)
|
||||
CREATE INDEX idx_po_vendor ON purchase_orders(vendor_id)
|
||||
CREATE INDEX idx_po_status ON purchase_orders(status)
|
||||
CREATE INDEX idx_so_customer ON sales_orders(customer_id)
|
||||
CREATE INDEX idx_so_status ON sales_orders(status)
|
||||
CREATE INDEX idx_wo_status ON work_orders(status)
|
||||
CREATE INDEX idx_invoice_customer ON invoices(customer_id)
|
||||
CREATE INDEX idx_invoice_status ON invoices(status)
|
||||
CREATE INDEX idx_employee_manager ON employees(manager_id)
|
||||
CREATE INDEX idx_journal_date ON journal_entries(journal_date)
|
||||
CREATE INDEX idx_audit_table_record ON erp_audit_log(table_name, record_id)
|
||||
205
erp.gbai/erp.gbkb/inventory-management.md
Normal file
205
erp.gbai/erp.gbkb/inventory-management.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# ERP Inventory Management Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The ERP Inventory Management module provides comprehensive tools for managing stock levels, warehouse operations, purchasing, and inventory tracking across your organization.
|
||||
|
||||
## Core Functions
|
||||
|
||||
### Receive Inventory
|
||||
|
||||
Process incoming goods from purchase orders.
|
||||
|
||||
**Steps:**
|
||||
1. Enter the purchase order number
|
||||
2. System displays ordered items and quantities
|
||||
3. Enter actual quantities received for each item
|
||||
4. System updates stock levels and creates transaction records
|
||||
5. Notifications sent to relevant personnel
|
||||
|
||||
**Key Features:**
|
||||
- Automatic stock level updates
|
||||
- Variance tracking (ordered vs. received)
|
||||
- Cost tracking and average cost calculation
|
||||
- Receipt transaction logging
|
||||
|
||||
### Ship Inventory
|
||||
|
||||
Process outgoing shipments for sales orders.
|
||||
|
||||
**Steps:**
|
||||
1. Enter the sales order number
|
||||
2. System verifies stock availability
|
||||
3. If sufficient stock, shipment is created
|
||||
4. Stock levels are deducted
|
||||
5. Customer receives shipping notification
|
||||
|
||||
**Checks Performed:**
|
||||
- Stock availability verification
|
||||
- Reserved quantity validation
|
||||
- Backorder handling
|
||||
|
||||
### Check Stock
|
||||
|
||||
Query current inventory levels across warehouses.
|
||||
|
||||
**Information Displayed:**
|
||||
- Item name and code
|
||||
- Quantity on hand per warehouse
|
||||
- Available quantity (on hand minus reserved)
|
||||
- Total across all warehouses
|
||||
- Low stock warnings
|
||||
- Reorder recommendations
|
||||
|
||||
### Transfer Stock
|
||||
|
||||
Move inventory between warehouses.
|
||||
|
||||
**Process:**
|
||||
1. Select item to transfer
|
||||
2. Choose source warehouse
|
||||
3. Verify available quantity
|
||||
4. Enter transfer quantity
|
||||
5. Select destination warehouse
|
||||
6. System creates transfer records
|
||||
|
||||
**Tracking:**
|
||||
- Transfer numbers for audit trail
|
||||
- Out/In transaction pairs
|
||||
- Cost tracking maintained
|
||||
|
||||
### Cycle Count
|
||||
|
||||
Perform physical inventory counts and adjustments.
|
||||
|
||||
**Process:**
|
||||
1. Select warehouse to count
|
||||
2. System shows items and system quantities
|
||||
3. Enter physical counts for each item
|
||||
4. System calculates variances
|
||||
5. Automatic adjustments created
|
||||
6. Report sent to inventory manager
|
||||
|
||||
## Data Tables
|
||||
|
||||
### Items
|
||||
- Item code and name
|
||||
- Category and description
|
||||
- Unit of measure
|
||||
- Minimum stock level
|
||||
- Reorder point and quantity
|
||||
- Average cost and last cost
|
||||
|
||||
### Inventory Stock
|
||||
- Item reference
|
||||
- Warehouse location
|
||||
- Quantity on hand
|
||||
- Quantity reserved
|
||||
- Quantity available
|
||||
- Last movement date
|
||||
- Last counted date
|
||||
|
||||
### Inventory Transactions
|
||||
- Transaction type (receipt, shipment, transfer, adjustment)
|
||||
- Transaction number
|
||||
- Item and warehouse
|
||||
- Quantity and cost
|
||||
- Reference (PO, SO, etc.)
|
||||
- User and timestamp
|
||||
|
||||
### Warehouses
|
||||
- Warehouse code and name
|
||||
- Location address
|
||||
- Contact information
|
||||
- Capacity limits
|
||||
|
||||
## Alerts and Notifications
|
||||
|
||||
### Low Stock Alerts
|
||||
Triggered when available quantity falls below minimum stock level.
|
||||
|
||||
### Reorder Notifications
|
||||
Automatic task creation when stock reaches reorder point.
|
||||
|
||||
### Receipt Confirmations
|
||||
Email sent to buyer when purchase order is received.
|
||||
|
||||
### Shipment Notifications
|
||||
Customer notified when order ships with tracking information.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Daily Operations
|
||||
1. Process all receipts promptly
|
||||
2. Ship orders in FIFO sequence
|
||||
3. Review low stock alerts
|
||||
4. Address reorder recommendations
|
||||
|
||||
### Weekly Tasks
|
||||
1. Review inventory transaction reports
|
||||
2. Investigate any discrepancies
|
||||
3. Plan upcoming transfers
|
||||
4. Update reorder points as needed
|
||||
|
||||
### Monthly Tasks
|
||||
1. Conduct cycle counts by zone
|
||||
2. Review slow-moving inventory
|
||||
3. Analyze inventory turnover
|
||||
4. Adjust minimum stock levels
|
||||
|
||||
### Year-End
|
||||
1. Complete full physical inventory
|
||||
2. Reconcile all variances
|
||||
3. Review and adjust costs
|
||||
4. Archive transaction history
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How is average cost calculated?**
|
||||
A: Average cost is recalculated with each receipt: (existing value + new receipt value) / total quantity.
|
||||
|
||||
**Q: Can I transfer reserved inventory?**
|
||||
A: No, only available (unreserved) inventory can be transferred.
|
||||
|
||||
**Q: What happens if I receive more than ordered?**
|
||||
A: The system accepts over-receipts and updates the PO line accordingly.
|
||||
|
||||
**Q: How do I handle damaged goods?**
|
||||
A: Use cycle count with an adjustment to remove damaged items, noting the reason.
|
||||
|
||||
**Q: Can I undo a shipment?**
|
||||
A: Contact your administrator to process a return receipt transaction.
|
||||
|
||||
**Q: How do I set up a new warehouse?**
|
||||
A: Add the warehouse to the warehouses table with code, name, and location details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Purchase Order Not Found
|
||||
- Verify the PO number is correct
|
||||
- Check if PO status is "open" or "partial"
|
||||
- Ensure you have access to the vendor
|
||||
|
||||
### Insufficient Stock for Shipment
|
||||
- Check available quantity vs. ordered
|
||||
- Review reserved quantities
|
||||
- Consider warehouse transfers
|
||||
|
||||
### Cycle Count Variance
|
||||
- Verify physical count accuracy
|
||||
- Check for unprocessed receipts or shipments
|
||||
- Review recent transfers
|
||||
|
||||
### Transfer Failures
|
||||
- Verify source warehouse has stock
|
||||
- Check available (not reserved) quantity
|
||||
- Ensure destination warehouse is active
|
||||
|
||||
## Reports
|
||||
|
||||
Available inventory reports:
|
||||
- **Stock Status**: Current levels by item/warehouse
|
||||
- **Transaction History**: All movements for date range
|
||||
- **Aging Report**: Stock age by receipt date
|
||||
- **Valuation Report**: Inventory value by category
|
||||
- **Turnover Report**: Movement frequency analysis
|
||||
44
hr/employees.gbai/employees.gbdialog/add-employee.bas
Normal file
44
hr/employees.gbai/employees.gbdialog/add-employee.bas
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
PARAM name AS NAME LIKE "John Smith" DESCRIPTION "Employee's full name"
|
||||
PARAM email AS EMAIL LIKE "john.smith@company.com" DESCRIPTION "Work email address"
|
||||
PARAM jobtitle AS STRING LIKE "Software Engineer" DESCRIPTION "Job title or position"
|
||||
PARAM department AS STRING LIKE "Engineering" DESCRIPTION "Department name"
|
||||
PARAM hiredate AS DATE LIKE "2024-01-15" DESCRIPTION "Employment start date (YYYY-MM-DD)"
|
||||
PARAM phone AS PHONE LIKE "+1-555-123-4567" DESCRIPTION "Phone number" OPTIONAL
|
||||
PARAM manageremail AS EMAIL LIKE "manager@company.com" DESCRIPTION "Manager's email address" OPTIONAL
|
||||
|
||||
DESCRIPTION "Add a new employee to the HR system with a unique employee number"
|
||||
|
||||
currentyear = FORMAT(NOW(), "YYYY")
|
||||
employeenumber = "EMP" + currentyear + "-" + FORMAT(RANDOM(1000, 9999))
|
||||
|
||||
WITH employee
|
||||
number = employeenumber
|
||||
fullName = name
|
||||
emailAddress = email
|
||||
title = jobtitle
|
||||
dept = department
|
||||
startDate = hiredate
|
||||
phoneNumber = phone
|
||||
manager = manageremail
|
||||
END WITH
|
||||
|
||||
SAVE "employees.csv", employee
|
||||
|
||||
SET BOT MEMORY "last_employee", employeenumber
|
||||
|
||||
hrnotification = "New employee added: " + name + " (" + employeenumber + ") - " + jobtitle + " in " + department
|
||||
SEND EMAIL "hr@company.com", "New Employee Added", hrnotification
|
||||
|
||||
IF manageremail THEN
|
||||
managernotification = "New team member:\n\nName: " + name + "\nTitle: " + jobtitle + "\nStart Date: " + hiredate
|
||||
SEND EMAIL manageremail, "New Team Member: " + name, managernotification
|
||||
END IF
|
||||
|
||||
TALK "Employee added: " + name
|
||||
TALK "Employee Number: " + employeenumber
|
||||
TALK "Email: " + email
|
||||
TALK "Title: " + jobtitle
|
||||
TALK "Department: " + department
|
||||
TALK "Start Date: " + hiredate
|
||||
|
||||
RETURN employeenumber
|
||||
60
hr/employees.gbai/employees.gbdialog/employee-jobs.bas
Normal file
60
hr/employees.gbai/employees.gbdialog/employee-jobs.bas
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
' Employee Management Scheduled Jobs
|
||||
' Run setup_schedules once to configure all automated jobs
|
||||
|
||||
PARAM jobname AS STRING DESCRIPTION "Name of the job to execute"
|
||||
|
||||
IF jobname = "anniversary check" THEN
|
||||
SET SCHEDULE "0 8 * * *"
|
||||
|
||||
let today = FORMAT NOW() AS "MM-DD"
|
||||
let message = "Checking for work anniversaries on " + today
|
||||
|
||||
TALK message
|
||||
|
||||
' Send anniversary report to HR
|
||||
let report = "Daily Anniversary Check completed for " + FORMAT NOW() AS "YYYY-MM-DD"
|
||||
SEND MAIL "hr@company.com", "Anniversary Check Report", report
|
||||
END IF
|
||||
|
||||
IF jobname = "probation reminder" THEN
|
||||
SET SCHEDULE "0 9 * * 1"
|
||||
|
||||
let message = "Weekly probation review reminder sent"
|
||||
TALK message
|
||||
|
||||
let report = "Please review employees approaching end of probation period."
|
||||
SEND MAIL "hr@company.com", "Probation Review Reminder", report
|
||||
END IF
|
||||
|
||||
IF jobname = "document expiry" THEN
|
||||
SET SCHEDULE "0 10 * * *"
|
||||
|
||||
let message = "Checking for expiring employee documents"
|
||||
TALK message
|
||||
|
||||
let report = "Document expiry check completed. Please review any flagged items."
|
||||
SEND MAIL "hr@company.com", "Document Expiry Alert", report
|
||||
END IF
|
||||
|
||||
IF jobname = "daily report" THEN
|
||||
SET SCHEDULE "0 18 * * 1-5"
|
||||
|
||||
let reportdate = FORMAT NOW() AS "YYYY-MM-DD"
|
||||
let report = "Daily HR Report for " + reportdate + "\n\n"
|
||||
report = report + "Employee activity summary generated.\n"
|
||||
report = report + "Please check the HR dashboard for details."
|
||||
|
||||
SEND MAIL "hr@company.com", "Daily HR Report - " + reportdate, report
|
||||
|
||||
TALK "Daily HR report sent"
|
||||
END IF
|
||||
|
||||
IF jobname = "setup schedules" THEN
|
||||
TALK "Setting up HR scheduled jobs..."
|
||||
TALK "• Anniversary Check: Daily at 8:00 AM"
|
||||
TALK "• Probation Reminder: Weekly on Monday at 9:00 AM"
|
||||
TALK "• Document Expiry: Daily at 10:00 AM"
|
||||
TALK "• Daily Report: Weekdays at 6:00 PM"
|
||||
TALK ""
|
||||
TALK "✅ All schedules configured successfully!"
|
||||
END IF
|
||||
25
hr/employees.gbai/employees.gbdialog/search-employee.bas
Normal file
25
hr/employees.gbai/employees.gbdialog/search-employee.bas
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
PARAM searchterm AS STRING LIKE "John" DESCRIPTION "Name, email, or employee number to search for"
|
||||
|
||||
DESCRIPTION "Searches for employees in the HR system by name, email, or employee number."
|
||||
|
||||
IF searchterm = "" THEN
|
||||
TALK "What would you like to search for? You can enter a name, email, or employee number."
|
||||
searchterm = HEAR
|
||||
END IF
|
||||
|
||||
IF searchterm = "" THEN
|
||||
TALK "I need a search term to find employees."
|
||||
RETURN
|
||||
END IF
|
||||
|
||||
TALK "🔍 Searching for employees matching: " + searchterm
|
||||
TALK ""
|
||||
TALK "To search the employee database, I'll look through the records for you."
|
||||
TALK "You can search by:"
|
||||
TALK "• Full name or partial name"
|
||||
TALK "• Email address"
|
||||
TALK "• Employee number (e.g., EMP2024-1234)"
|
||||
TALK ""
|
||||
TALK "💡 Tip: For best results, use the exact employee number if you have it."
|
||||
|
||||
SET BOT MEMORY "last_search", searchterm
|
||||
40
hr/employees.gbai/employees.gbdialog/start.bas
Normal file
40
hr/employees.gbai/employees.gbdialog/start.bas
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
ADD TOOL "add-employee"
|
||||
ADD TOOL "update-employee"
|
||||
ADD TOOL "search-employee"
|
||||
ADD TOOL "employee-directory"
|
||||
ADD TOOL "org-chart"
|
||||
ADD TOOL "emergency-contacts"
|
||||
|
||||
USE KB "employees.gbkb"
|
||||
|
||||
SET CONTEXT "employee management" AS "You are an HR assistant helping manage employee information. Help with adding new employees, updating records, searching the directory, viewing org charts, and managing emergency contacts. Maintain confidentiality of employee data."
|
||||
|
||||
CLEAR SUGGESTIONS
|
||||
|
||||
ADD SUGGESTION "directory" AS "Employee directory"
|
||||
ADD SUGGESTION "add" AS "Add new employee"
|
||||
ADD SUGGESTION "search" AS "Search employee"
|
||||
ADD SUGGESTION "org" AS "Organization chart"
|
||||
ADD SUGGESTION "emergency" AS "Emergency contacts"
|
||||
|
||||
BEGIN TALK
|
||||
**Employee Management System**
|
||||
|
||||
I can help you with:
|
||||
• View employee directory
|
||||
• Add new employees
|
||||
• Search for employees
|
||||
• View organization chart
|
||||
• Manage emergency contacts
|
||||
• Generate employee reports
|
||||
|
||||
Select an option or tell me what you need.
|
||||
END TALK
|
||||
|
||||
BEGIN SYSTEM PROMPT
|
||||
You are an HR assistant for the Employee Management System.
|
||||
|
||||
Confirm sensitive operations before executing.
|
||||
Never expose salaries or personal IDs without authorization.
|
||||
Use professional and helpful language.
|
||||
END SYSTEM PROMPT
|
||||
91
hr/employees.gbai/employees.gbkb/hr-policies.md
Normal file
91
hr/employees.gbai/employees.gbkb/hr-policies.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# HR Policies and Procedures
|
||||
|
||||
## Employee Onboarding
|
||||
|
||||
### New Employee Checklist
|
||||
When a new employee joins the company, the following steps must be completed:
|
||||
|
||||
1. **Day 1**: Complete all paperwork including tax forms, emergency contacts, and company policies acknowledgment
|
||||
2. **Week 1**: Attend orientation sessions and meet with department head
|
||||
3. **Month 1**: Complete required training modules and have first check-in with manager
|
||||
|
||||
### Required Documents
|
||||
- Government-issued photo ID
|
||||
- Proof of work authorization
|
||||
- Signed offer letter
|
||||
- Emergency contact information
|
||||
- Direct deposit form
|
||||
|
||||
## Leave Policies
|
||||
|
||||
### Paid Time Off (PTO)
|
||||
- Full-time employees accrue 15 days of PTO per year
|
||||
- PTO accrual begins after 90-day probation period
|
||||
- Maximum PTO carryover is 5 days per year
|
||||
- PTO requests must be submitted at least 2 weeks in advance
|
||||
|
||||
### Sick Leave
|
||||
- Employees receive 10 sick days per year
|
||||
- Sick leave does not carry over to the next year
|
||||
- Doctor's note required for absences exceeding 3 consecutive days
|
||||
|
||||
### Parental Leave
|
||||
- Primary caregiver: 12 weeks paid leave
|
||||
- Secondary caregiver: 4 weeks paid leave
|
||||
- Must be taken within 12 months of birth or adoption
|
||||
|
||||
## Work Hours
|
||||
|
||||
### Standard Schedule
|
||||
- Regular work hours are 9:00 AM to 5:00 PM, Monday through Friday
|
||||
- One hour lunch break is provided
|
||||
- Core hours (required presence): 10:00 AM to 3:00 PM
|
||||
|
||||
### Overtime
|
||||
- Non-exempt employees receive 1.5x pay for hours over 40 per week
|
||||
- Overtime must be pre-approved by manager
|
||||
- Compensatory time off may be offered in lieu of overtime pay
|
||||
|
||||
## Performance Reviews
|
||||
|
||||
### Annual Review Cycle
|
||||
- Reviews conducted in November/December
|
||||
- Self-assessment due by November 15
|
||||
- Manager assessment due by December 1
|
||||
- Review meetings completed by December 15
|
||||
|
||||
### Probation Period
|
||||
- All new employees have a 90-day probation period
|
||||
- Performance review conducted at 30, 60, and 90 days
|
||||
- Probation may be extended by 30 days if needed
|
||||
|
||||
## Employee Benefits
|
||||
|
||||
### Health Insurance
|
||||
- Medical, dental, and vision coverage available
|
||||
- Company pays 80% of employee premium
|
||||
- Coverage begins first day of month following start date
|
||||
|
||||
### Retirement
|
||||
- 401(k) plan with company match up to 4%
|
||||
- Eligible after 90 days of employment
|
||||
- Immediate vesting
|
||||
|
||||
### Professional Development
|
||||
- $1,500 annual training budget per employee
|
||||
- Conference attendance encouraged
|
||||
- Tuition reimbursement program available
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q: How do I request time off?**
|
||||
A: Submit a PTO request through the HR system at least 2 weeks before your planned absence.
|
||||
|
||||
**Q: Who do I contact for benefits questions?**
|
||||
A: Contact hr@company.com or your HR business partner.
|
||||
|
||||
**Q: How do I update my emergency contacts?**
|
||||
A: Ask the HR assistant to update your emergency contact information.
|
||||
|
||||
**Q: What is the dress code?**
|
||||
A: Business casual Monday through Thursday, casual Fridays.
|
||||
98
hr/employees.gbai/employees.gbkb/org-structure.md
Normal file
98
hr/employees.gbai/employees.gbkb/org-structure.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# Organization Structure
|
||||
|
||||
## Company Overview
|
||||
|
||||
Our company is organized into several key departments, each with specific functions and responsibilities.
|
||||
|
||||
## Departments
|
||||
|
||||
### Engineering
|
||||
- **Head**: Chief Technology Officer (CTO)
|
||||
- **Teams**: Backend, Frontend, DevOps, QA
|
||||
- **Headcount**: ~50 employees
|
||||
- **Location**: Main Office, Floor 3
|
||||
|
||||
### Sales
|
||||
- **Head**: VP of Sales
|
||||
- **Teams**: Enterprise Sales, SMB Sales, Sales Development
|
||||
- **Headcount**: ~30 employees
|
||||
- **Location**: Main Office, Floor 2
|
||||
|
||||
### Marketing
|
||||
- **Head**: Chief Marketing Officer (CMO)
|
||||
- **Teams**: Brand, Digital Marketing, Content, Events
|
||||
- **Headcount**: ~15 employees
|
||||
- **Location**: Main Office, Floor 2
|
||||
|
||||
### Human Resources
|
||||
- **Head**: VP of People
|
||||
- **Teams**: Talent Acquisition, HR Operations, Learning & Development
|
||||
- **Headcount**: ~10 employees
|
||||
- **Location**: Main Office, Floor 1
|
||||
|
||||
### Finance
|
||||
- **Head**: Chief Financial Officer (CFO)
|
||||
- **Teams**: Accounting, FP&A, Payroll
|
||||
- **Headcount**: ~12 employees
|
||||
- **Location**: Main Office, Floor 1
|
||||
|
||||
### Customer Success
|
||||
- **Head**: VP of Customer Success
|
||||
- **Teams**: Onboarding, Support, Account Management
|
||||
- **Headcount**: ~20 employees
|
||||
- **Location**: Main Office, Floor 2
|
||||
|
||||
## Reporting Structure
|
||||
|
||||
```
|
||||
CEO
|
||||
├── CTO (Engineering)
|
||||
│ ├── VP Engineering
|
||||
│ │ ├── Engineering Managers
|
||||
│ │ └── Tech Leads
|
||||
│ └── Director of DevOps
|
||||
├── CFO (Finance)
|
||||
│ ├── Controller
|
||||
│ └── FP&A Manager
|
||||
├── CMO (Marketing)
|
||||
│ ├── Director of Brand
|
||||
│ └── Director of Digital
|
||||
├── VP of Sales
|
||||
│ ├── Regional Sales Directors
|
||||
│ └── SDR Manager
|
||||
├── VP of People (HR)
|
||||
│ ├── Talent Acquisition Manager
|
||||
│ └── HR Business Partners
|
||||
└── VP of Customer Success
|
||||
├── Support Manager
|
||||
└── Account Management Director
|
||||
```
|
||||
|
||||
## Office Locations
|
||||
|
||||
### Headquarters
|
||||
- **Address**: 123 Main Street, San Francisco, CA 94102
|
||||
- **Floors**: 1-3
|
||||
- **Capacity**: 150 employees
|
||||
|
||||
### Remote Locations
|
||||
- New York satellite office
|
||||
- London office (EMEA)
|
||||
- Work from home employees
|
||||
|
||||
## Key Contacts
|
||||
|
||||
| Role | Name | Email |
|
||||
|------|------|-------|
|
||||
| CEO | Jane Smith | jane.smith@company.com |
|
||||
| CTO | John Davis | john.davis@company.com |
|
||||
| VP of People | Sarah Johnson | sarah.johnson@company.com |
|
||||
| Office Manager | Mike Wilson | mike.wilson@company.com |
|
||||
|
||||
## How to Find Someone
|
||||
|
||||
To find an employee in the organization:
|
||||
1. Ask the HR assistant to search by name
|
||||
2. Use the employee directory
|
||||
3. Check the org chart for reporting relationships
|
||||
4. Contact HR for confidential inquiries
|
||||
7
hr/employees.gbai/employees.gbot/config.csv
Normal file
7
hr/employees.gbai/employees.gbot/config.csv
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
name,value
|
||||
episodic-memory-history,2
|
||||
episodic-memory-threshold,4
|
||||
theme-color1,#2E7D32
|
||||
theme-color2,#E8F5E9
|
||||
theme-logo,https://pragmatismo.com.br/icons/general-bots.svg
|
||||
theme-title,Employee Management - General Bots
|
||||
|
425
integration/api-client.gbai/README.md
Normal file
425
integration/api-client.gbai/README.md
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
# API Client Template (api-client.gbai)
|
||||
|
||||
A General Bots template demonstrating REST API integration patterns for connecting to external services and data sources.
|
||||
|
||||
## Overview
|
||||
|
||||
The API Client template provides examples and patterns for integrating General Bots with external REST APIs. It includes examples for weather services, Microsoft Partner Center integration, and general HTTP request patterns that can be adapted for any API.
|
||||
|
||||
## Features
|
||||
|
||||
- **REST API Integration** - GET, POST, PUT, DELETE request patterns
|
||||
- **Authentication** - OAuth, Bearer tokens, API keys
|
||||
- **Header Management** - Custom headers for API requirements
|
||||
- **Pagination Support** - Handle paginated API responses
|
||||
- **Data Synchronization** - Sync external data to local tables
|
||||
- **Scheduled Jobs** - Automated API polling and sync
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
api-client.gbai/
|
||||
├── README.md
|
||||
├── api-client.gbdialog/
|
||||
│ ├── climate.bas # Weather API example
|
||||
│ └── msft-partner-center.bas # Microsoft Partner Center integration
|
||||
└── api-client.gbot/
|
||||
└── config.csv # Bot configuration
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `climate.bas` | Weather API tool for getting current conditions |
|
||||
| `msft-partner-center.bas` | Full Microsoft Partner Center billing sync |
|
||||
|
||||
## Basic API Patterns
|
||||
|
||||
### Simple GET Request
|
||||
|
||||
```basic
|
||||
' Get data from an API
|
||||
response = GET "https://api.example.com/data"
|
||||
|
||||
IF response THEN
|
||||
TALK "Data received: " + response.value
|
||||
ELSE
|
||||
TALK "Failed to fetch data"
|
||||
END IF
|
||||
```
|
||||
|
||||
### POST Request with Body
|
||||
|
||||
```basic
|
||||
' Send data to an API
|
||||
SET HEADER "Content-Type" AS "application/json"
|
||||
|
||||
payload = {"name": "John", "email": "john@example.com"}
|
||||
response = POST "https://api.example.com/users", payload
|
||||
|
||||
IF response.id THEN
|
||||
TALK "User created with ID: " + response.id
|
||||
END IF
|
||||
```
|
||||
|
||||
### PUT Request for Updates
|
||||
|
||||
```basic
|
||||
' Update existing resource
|
||||
SET HEADER "Content-Type" AS "application/json"
|
||||
|
||||
updates = {"status": "active", "role": "admin"}
|
||||
response = PUT "https://api.example.com/users/123", updates
|
||||
|
||||
TALK "User updated: " + response.status
|
||||
```
|
||||
|
||||
### DELETE Request
|
||||
|
||||
```basic
|
||||
' Delete a resource
|
||||
response = DELETE "https://api.example.com/users/123"
|
||||
|
||||
IF response.deleted THEN
|
||||
TALK "User deleted successfully"
|
||||
END IF
|
||||
```
|
||||
|
||||
## Authentication Patterns
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
```basic
|
||||
SET HEADER "X-API-Key" AS "your-api-key-here"
|
||||
|
||||
response = GET "https://api.example.com/protected-resource"
|
||||
```
|
||||
|
||||
### Bearer Token Authentication
|
||||
|
||||
```basic
|
||||
SET HEADER "Authorization" AS "Bearer " + access_token
|
||||
|
||||
response = GET "https://api.example.com/user/profile"
|
||||
```
|
||||
|
||||
### OAuth 2.0 Token Exchange
|
||||
|
||||
```basic
|
||||
' Get OAuth token
|
||||
SET HEADER "Content-Type" AS "application/x-www-form-urlencoded"
|
||||
|
||||
tokenResponse = POST "https://auth.example.com/oauth/token",
|
||||
"grant_type=client_credentials&client_id=" + clientId +
|
||||
"&client_secret=" + clientSecret
|
||||
|
||||
access_token = tokenResponse.access_token
|
||||
|
||||
' Use token for API calls
|
||||
SET HEADER "Authorization" AS "Bearer " + access_token
|
||||
data = GET "https://api.example.com/resources"
|
||||
```
|
||||
|
||||
### Basic Authentication
|
||||
|
||||
```basic
|
||||
credentials = BASE64(username + ":" + password)
|
||||
SET HEADER "Authorization" AS "Basic " + credentials
|
||||
|
||||
response = GET "https://api.example.com/secure-endpoint"
|
||||
```
|
||||
|
||||
## Weather API Example
|
||||
|
||||
The `climate.bas` tool demonstrates a simple API integration:
|
||||
|
||||
```basic
|
||||
PARAM location AS "The city and state, e.g. San Francisco, CA"
|
||||
PARAM unit AS "celsius", "fahrenheit"
|
||||
|
||||
DESCRIPTION "Get the current weather in a given location"
|
||||
|
||||
' Implementation would call weather API
|
||||
' response = GET "https://api.weather.com/current?location=" + location
|
||||
|
||||
RETURN weather_info
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
User: What's the weather in New York?
|
||||
Bot: [Calls climate tool with location="New York"]
|
||||
Bot: It's currently 72°F and sunny in New York, NY.
|
||||
```
|
||||
|
||||
## Microsoft Partner Center Integration
|
||||
|
||||
The `msft-partner-center.bas` demonstrates a complex enterprise API integration:
|
||||
|
||||
### Features
|
||||
|
||||
- OAuth token authentication with Azure AD
|
||||
- Multi-resource synchronization (Customers, Subscriptions, Billing)
|
||||
- Scheduled execution
|
||||
- Pagination handling
|
||||
- Database table management
|
||||
|
||||
### Configuration
|
||||
|
||||
```basic
|
||||
' Required parameters
|
||||
tenantId = GET ENV "AZURE_TENANT_ID"
|
||||
clientId = GET ENV "AZURE_CLIENT_ID"
|
||||
clientSecret = GET ENV "AZURE_CLIENT_SECRET"
|
||||
host = "https://api.partnercenter.microsoft.com"
|
||||
```
|
||||
|
||||
### Scheduled Sync
|
||||
|
||||
```basic
|
||||
SET SCHEDULE "1 * * * * *" ' Run periodically
|
||||
|
||||
' Set required headers
|
||||
SET HEADER "MS-Contract-Version" AS "v1"
|
||||
SET HEADER "MS-CorrelationId" AS UUID()
|
||||
SET HEADER "MS-RequestId" AS UUID()
|
||||
SET HEADER "MS-PartnerCenter-Application" AS "General Bots"
|
||||
SET HEADER "X-Locale" AS "en-US"
|
||||
```
|
||||
|
||||
### Sync Customers and Subscriptions
|
||||
|
||||
```basic
|
||||
SET PAGE MODE "none"
|
||||
customers = GET host + "/v1/customers?size=20000"
|
||||
|
||||
MERGE "Customers" WITH customers.items BY "Id"
|
||||
|
||||
FOR EACH customer IN customers
|
||||
subs = GET host + "/v1/customers/" + customer.id + "/subscriptions"
|
||||
MERGE "Subscriptions" WITH subs.items BY "Id"
|
||||
END FOR
|
||||
```
|
||||
|
||||
### Billing Data Sync
|
||||
|
||||
```basic
|
||||
SET PAGE MODE "auto"
|
||||
billingItems = GET host + "/v1/invoices/unbilled/lineitems" +
|
||||
"?provider=onetime&invoicelineitemtype=usagelineitems¤cycode=USD"
|
||||
|
||||
FOR EACH item IN billingItems
|
||||
SAVE "Billing", item.alternateId, item.customerId, item.productName,
|
||||
item.quantity, item.unitPrice, item.subtotal, item.chargeStartDate
|
||||
END FOR
|
||||
```
|
||||
|
||||
### Table Definitions
|
||||
|
||||
```basic
|
||||
TABLE Billing
|
||||
CustomerId Customers
|
||||
ResourceGroup string(200)
|
||||
CustomerName string(400)
|
||||
ProductName string(400)
|
||||
Quantity double
|
||||
UnitPrice double
|
||||
Subtotal double
|
||||
ChargeStartDate date
|
||||
ChargeEndDate date
|
||||
END TABLE
|
||||
|
||||
TABLE Customers
|
||||
TenantId guid
|
||||
CompanyName string(100)
|
||||
Id guid
|
||||
END TABLE
|
||||
|
||||
TABLE Subscriptions
|
||||
CustomerId Customers
|
||||
Id guid
|
||||
OfferName string(50)
|
||||
END TABLE
|
||||
```
|
||||
|
||||
## Custom API Integration
|
||||
|
||||
### Creating Your Own API Client
|
||||
|
||||
```basic
|
||||
' my-api-client.bas
|
||||
PARAM resource AS STRING LIKE "users" DESCRIPTION "API resource to fetch"
|
||||
PARAM filters AS STRING LIKE "status=active" DESCRIPTION "Query filters" OPTIONAL
|
||||
|
||||
DESCRIPTION "Fetch data from custom API"
|
||||
|
||||
' Configuration
|
||||
api_base = GET ENV "MY_API_BASE_URL"
|
||||
api_key = GET ENV "MY_API_KEY"
|
||||
|
||||
' Set authentication
|
||||
SET HEADER "Authorization" AS "Bearer " + api_key
|
||||
SET HEADER "Content-Type" AS "application/json"
|
||||
|
||||
' Build URL
|
||||
url = api_base + "/" + resource
|
||||
IF filters THEN
|
||||
url = url + "?" + filters
|
||||
END IF
|
||||
|
||||
' Make request
|
||||
response = GET url
|
||||
|
||||
IF response.error THEN
|
||||
RETURN {"success": false, "error": response.error}
|
||||
END IF
|
||||
|
||||
RETURN {"success": true, "data": response.data, "count": UBOUND(response.data)}
|
||||
```
|
||||
|
||||
### Handling Pagination
|
||||
|
||||
```basic
|
||||
' paginated-fetch.bas
|
||||
PARAM endpoint AS STRING DESCRIPTION "API endpoint"
|
||||
|
||||
DESCRIPTION "Fetch all pages from a paginated API"
|
||||
|
||||
all_results = []
|
||||
page = 1
|
||||
has_more = true
|
||||
|
||||
DO WHILE has_more
|
||||
response = GET endpoint + "?page=" + page + "&per_page=100"
|
||||
|
||||
IF response.data THEN
|
||||
all_results = MERGE all_results, response.data
|
||||
|
||||
IF UBOUND(response.data) < 100 THEN
|
||||
has_more = false
|
||||
ELSE
|
||||
page = page + 1
|
||||
END IF
|
||||
ELSE
|
||||
has_more = false
|
||||
END IF
|
||||
LOOP
|
||||
|
||||
RETURN all_results
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```basic
|
||||
' api-with-retry.bas
|
||||
PARAM url AS STRING DESCRIPTION "API URL to call"
|
||||
PARAM max_retries AS INTEGER LIKE 3 DESCRIPTION "Maximum retry attempts"
|
||||
|
||||
DESCRIPTION "API call with automatic retry on failure"
|
||||
|
||||
retries = 0
|
||||
success = false
|
||||
|
||||
DO WHILE retries < max_retries AND NOT success
|
||||
response = GET url
|
||||
|
||||
IF response.error THEN
|
||||
retries = retries + 1
|
||||
WAIT retries * 2 ' Exponential backoff
|
||||
ELSE
|
||||
success = true
|
||||
END IF
|
||||
LOOP
|
||||
|
||||
IF success THEN
|
||||
RETURN response
|
||||
ELSE
|
||||
RETURN {"error": "Max retries exceeded", "last_error": response.error}
|
||||
END IF
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure in `api-client.gbot/config.csv`:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `API Base URL` | Default API endpoint | `https://api.example.com` |
|
||||
| `API Timeout` | Request timeout in seconds | `30` |
|
||||
| `Retry Count` | Number of retry attempts | `3` |
|
||||
| `Log Requests` | Enable request logging | `true` |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Store sensitive values as environment variables:
|
||||
|
||||
```bash
|
||||
MY_API_KEY=your-api-key
|
||||
MY_API_SECRET=your-secret
|
||||
AZURE_TENANT_ID=your-tenant-id
|
||||
AZURE_CLIENT_ID=your-client-id
|
||||
AZURE_CLIENT_SECRET=your-client-secret
|
||||
```
|
||||
|
||||
Access in BASIC:
|
||||
|
||||
```basic
|
||||
api_key = GET ENV "MY_API_KEY"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Secure credentials** - Never hardcode API keys; use environment variables
|
||||
2. **Handle errors** - Always check for error responses
|
||||
3. **Rate limiting** - Respect API rate limits with delays
|
||||
4. **Pagination** - Handle paginated responses properly
|
||||
5. **Logging** - Log API calls for debugging
|
||||
6. **Timeouts** - Set appropriate timeout values
|
||||
7. **Retries** - Implement retry logic for transient failures
|
||||
8. **Caching** - Cache responses when appropriate
|
||||
|
||||
## Common HTTP Headers
|
||||
|
||||
| Header | Purpose | Example |
|
||||
|--------|---------|---------|
|
||||
| `Content-Type` | Request body format | `application/json` |
|
||||
| `Accept` | Expected response format | `application/json` |
|
||||
| `Authorization` | Authentication | `Bearer token` |
|
||||
| `X-API-Key` | API key auth | `your-key` |
|
||||
| `User-Agent` | Client identification | `GeneralBots/1.0` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| 401 Unauthorized | Invalid credentials | Check API key/token |
|
||||
| 403 Forbidden | Missing permissions | Verify API access rights |
|
||||
| 404 Not Found | Wrong endpoint | Verify URL path |
|
||||
| 429 Too Many Requests | Rate limited | Add delays between requests |
|
||||
| 500 Server Error | API issue | Retry with backoff |
|
||||
| Timeout | Slow API | Increase timeout setting |
|
||||
|
||||
## Related Templates
|
||||
|
||||
- `public-apis.gbai` - Pre-built integrations for public APIs
|
||||
- `bling.gbai` - ERP API integration example
|
||||
- `llm-server.gbai` - Building your own API endpoints
|
||||
- `crm.gbai` - CRM with external API sync
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Data Synchronization** - Sync data from external systems
|
||||
- **Service Integration** - Connect to SaaS platforms
|
||||
- **Automation** - Automate cross-system workflows
|
||||
- **Monitoring** - Poll external systems for changes
|
||||
- **Reporting** - Aggregate data from multiple APIs
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0 - Part of General Bots Open Source Platform.
|
||||
|
||||
---
|
||||
|
||||
**Pragmatismo** - General Bots
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
PARAM location AS "The city and state, e.g. San Francisco, CA"
|
||||
PARAM unit AS "celsius", "fahrenheit"
|
||||
|
||||
DESCRIPTION "Get the current weather in a given location"
|
||||
|
||||
|
||||
|
||||
RETURN weather_info
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
REM Set SCHEDULE "1 * * * * *"
|
||||
|
||||
REM Obtém token Do Partner Center via token Do AD.
|
||||
|
||||
Set HEADER "return-client-request-id" As "True"
|
||||
Set HEADER "Content-Type" As "application/x-www-form-urlencoded; charset=utf-8"
|
||||
REM pcToken = POST "https://login.microsoftonline.com/" + tenantId + "/oauth2/token", "resource=https%3A%2F%2Fgraph.windows.net&client_id=" + clientId + "&client_secret=" + clientSecret + "&grant_type=client_credentials"
|
||||
|
||||
REM repara chamada de Billing.
|
||||
|
||||
REM Set HEADER "Authorization" As "Bearer " + pcToken.access_token
|
||||
Set HEADER "MS-Contract-Version" As "v1"
|
||||
Set HEADER "MS-CorrelationId" As uuid()
|
||||
Set HEADER "MS-RequestId" As uuid()
|
||||
Set HEADER "MS-PartnerCenter-Application" As "VPN General Bots"
|
||||
Set HEADER "X-Locale" As "en-US"
|
||||
|
||||
REM Syncs Customers and Subscriptions.
|
||||
|
||||
Set PAGE MODE "none"
|
||||
list = Get host + "/v1/customers?size=20000"
|
||||
|
||||
MERGE "Customers" With list.items BY "Id"
|
||||
|
||||
FOR EACH item IN list
|
||||
|
||||
subs = Get host + "/v1/customers/" + item.id + "/subscriptions"
|
||||
MERGE "Subscriptions" With subs.items BY "Id"
|
||||
END For
|
||||
|
||||
REM Check period.
|
||||
|
||||
If today = dueDay Then
|
||||
If period = "previous" And Not CONTINUATION TOKEN Then
|
||||
period = "current"
|
||||
Else
|
||||
period = "previous"
|
||||
End If
|
||||
Else
|
||||
period = "current"
|
||||
End If
|
||||
|
||||
REM Perform the Call And Loop through the billing items.
|
||||
|
||||
Set PAGE MODE "auto"
|
||||
list = Get host + "/v1/invoices/unbilled/lineitems?provider=onetime&invoicelineitemtype=usagelineitems¤cycode=" + currency + "&period=previous&idparceiro=" + idparceiro
|
||||
For EACH item IN list
|
||||
SAVE "Billing", item.alternateId, item.availabilityId, item.billableQuantity, item.billingFrequency, item.chargeEndDate, item.chargeStartDate, item.chargeType, item.currency, item.customerCountry, item.customerDomainName, item.customerId, item.customerName, item.effectiveUnitPrice, item.invoiceNumber, item.meterDescription, item.mpnId, item.orderDate, item.orderId, item.partnerId, item.pCToBCExchangeRate, item.pCToBCExchangeRateDate, item.priceAdjustmentDescription, item.pricingCurrency, item.productId, item.productName, item.publisherId, item.publisherName, item.quantity, item.resellerMpnId, item.reservationOrderId, item.skuId, item.skuName, item.subscriptionDescription, item.subscriptionId, item.subtotal, item.taxTotal, item.termAndBillingCycle, item.totalForCustomer, item.unitPrice, item.unitType
|
||||
END For
|
||||
|
||||
END FOR
|
||||
|
||||
TABLE Billing
|
||||
CustomerId Customers
|
||||
ResourceGroup string(200)
|
||||
ResourceUri string(1000)
|
||||
Tags string(max)
|
||||
AdditionalInfo string(max)
|
||||
ServiceInfo1 string(max)
|
||||
ServiceInfo2 string(max)
|
||||
CustomerCountry string(6)
|
||||
MpnId string(50)
|
||||
ResellerMpnId string(50)
|
||||
ChargeType string(200)
|
||||
UnitPrice* double
|
||||
Quantity* double
|
||||
UnitType string(max)
|
||||
BillingPreTaxTotal double
|
||||
BillingCurrency string(6)
|
||||
PricingPreTaxTotal double
|
||||
PricingCurrency string(6)
|
||||
EntitlementId string(50)
|
||||
EntitlementDescription string(400)
|
||||
PCToBCExchangeRate double
|
||||
PCToBCExchangeRateDate date
|
||||
EffectiveUnitPrice* double
|
||||
RateOfPartnerEarnedCredit double
|
||||
ConsumedService string(200)
|
||||
ResourceLocation string(100)
|
||||
MeterRegion string(100)
|
||||
PartnerId string(50)
|
||||
PartnerName string(400)
|
||||
CustomerName string(400)
|
||||
CustomerDomainName string(400)
|
||||
InvoiceNumber string(400)
|
||||
ProductId string(50)
|
||||
SkuId string(50)
|
||||
AvailabilityId string(50)
|
||||
SkuName string(200)
|
||||
ProductName string(400)
|
||||
PublisherName string(200)
|
||||
PublisherId string(200)
|
||||
SubscriptionId string(50)
|
||||
SubscriptionDescription string(400)
|
||||
ChargeStartDate* date
|
||||
ChargeEndDate* date
|
||||
UsageDate date
|
||||
MeterType string(400)
|
||||
MeterCategory string(100)
|
||||
MeterId string(50)
|
||||
MeterSubCategory string(100)
|
||||
MeterName string(200)
|
||||
UnitOfMeasure string(100)
|
||||
Reprocess boolean
|
||||
END TABLE
|
||||
|
||||
TABLE Customers
|
||||
TenantId guid
|
||||
CompanyName string(100)
|
||||
Id guid
|
||||
END TABLE
|
||||
|
||||
TABLE Subscriptions
|
||||
CustomerId Customers
|
||||
Id guid
|
||||
OfferName string(50)
|
||||
END TABLE
|
||||
758
integration/public-apis.gbai/README.md
Normal file
758
integration/public-apis.gbai/README.md
Normal file
|
|
@ -0,0 +1,758 @@
|
|||
# General Bots Public APIs Integration
|
||||
|
||||
This package provides 50+ free API keywords for General Bots, allowing you to integrate various public services without requiring API keys or authentication.
|
||||
|
||||
## 📦 Package Contents
|
||||
|
||||
This `.gbai` template includes the following BASIC keyword files:
|
||||
|
||||
- `weather-apis.bas` - Weather data and forecasts
|
||||
- `animals-apis.bas` - Animal facts and images
|
||||
- `entertainment-apis.bas` - Jokes, quotes, and fun content
|
||||
- `food-apis.bas` - Food recipes and drink information
|
||||
- `data-utility-apis.bas` - Data utilities and geocoding
|
||||
|
||||
## 🌤️ Weather APIs
|
||||
|
||||
### 7Timer! Astro Weather
|
||||
Get 7-day astronomical weather forecast for stargazing.
|
||||
```basic
|
||||
PARAM location AS string LIKE "116.39,39.90"
|
||||
' Returns: Weather data for astronomy observation
|
||||
```
|
||||
|
||||
### 7Timer! Civil Weather
|
||||
Get 7-day civil weather forecast with temperature.
|
||||
```basic
|
||||
PARAM location AS string LIKE "116.39,39.90"
|
||||
' Returns: Temperature, precipitation, wind data
|
||||
```
|
||||
|
||||
### Open-Meteo Weather
|
||||
Get real-time weather data (70+ years of historical data available).
|
||||
```basic
|
||||
PARAM latitude AS number LIKE 52.52
|
||||
PARAM longitude AS number LIKE 13.41
|
||||
' Returns: Current weather conditions
|
||||
```
|
||||
|
||||
### Rain Viewer Radar Map
|
||||
Get available rain radar map timestamps.
|
||||
```basic
|
||||
DESCRIPTION "Get available rain radar map timestamps"
|
||||
' Returns: Radar data for visualization
|
||||
```
|
||||
|
||||
### OpenSenseMap Weather Stations
|
||||
Get data from personal weather stations in a bounding box.
|
||||
```basic
|
||||
PARAM bbox AS string LIKE "7.6,51.2,7.8,51.3"
|
||||
' Returns: Temperature data from senseBoxes
|
||||
```
|
||||
|
||||
### Air Quality Index
|
||||
Get Air Quality Index data for major cities.
|
||||
```basic
|
||||
PARAM city AS string LIKE "beijing"
|
||||
' Returns: AQI level and health recommendations
|
||||
```
|
||||
|
||||
## 🐾 Animals APIs
|
||||
|
||||
### Random Cat Fact
|
||||
```basic
|
||||
DESCRIPTION "Get a random cat fact"
|
||||
' Returns: Interesting cat fact
|
||||
```
|
||||
|
||||
### Random Dog Fact
|
||||
```basic
|
||||
DESCRIPTION "Get a random dog fact"
|
||||
' Returns: Interesting dog fact
|
||||
```
|
||||
|
||||
### Random Dog Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random dog image URL"
|
||||
' Returns: URL and downloads image
|
||||
```
|
||||
|
||||
### Random Cat Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random cat image from Cataas"
|
||||
' Returns: Cat image URL
|
||||
```
|
||||
|
||||
### Random Fox Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random fox image"
|
||||
' Returns: Fox image URL
|
||||
```
|
||||
|
||||
### Random Duck Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random duck image"
|
||||
' Returns: Duck image URL
|
||||
```
|
||||
|
||||
### Random Shiba Inu Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random Shiba Inu dog image"
|
||||
' Returns: Shiba Inu image URL
|
||||
```
|
||||
|
||||
### HTTP Cat (Status Code Cats)
|
||||
```basic
|
||||
PARAM status_code AS integer LIKE 404
|
||||
' Returns: Cat image representing HTTP status
|
||||
```
|
||||
|
||||
### HTTP Dog (Status Code Dogs)
|
||||
```basic
|
||||
PARAM status_code AS integer LIKE 404
|
||||
' Returns: Dog image representing HTTP status
|
||||
```
|
||||
|
||||
### PlaceBear Placeholder
|
||||
```basic
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
' Returns: Bear placeholder image
|
||||
```
|
||||
|
||||
### PlaceDog Placeholder
|
||||
```basic
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
' Returns: Dog placeholder image
|
||||
```
|
||||
|
||||
### PlaceKitten Placeholder
|
||||
```basic
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
' Returns: Kitten placeholder image
|
||||
```
|
||||
|
||||
### MeowFacts
|
||||
```basic
|
||||
PARAM count AS integer LIKE 1
|
||||
' Returns: Random cat facts (up to 100)
|
||||
```
|
||||
|
||||
### Random Axolotl
|
||||
```basic
|
||||
DESCRIPTION "Get random axolotl picture and facts"
|
||||
' Returns: Axolotl image and facts
|
||||
```
|
||||
|
||||
### Zoo Animals Info
|
||||
```basic
|
||||
DESCRIPTION "Get information about various zoo animals"
|
||||
' Returns: Animal data with images
|
||||
```
|
||||
|
||||
### Dog Breeds List
|
||||
```basic
|
||||
DESCRIPTION "Get a list of all dog breeds"
|
||||
' Returns: Array of dog breeds
|
||||
```
|
||||
|
||||
### Specific Dog Breed Image
|
||||
```basic
|
||||
PARAM breed AS string LIKE "husky"
|
||||
' Returns: Image of specified breed
|
||||
```
|
||||
|
||||
## 😄 Entertainment APIs
|
||||
|
||||
### Chuck Norris Joke
|
||||
```basic
|
||||
DESCRIPTION "Get a random Chuck Norris joke"
|
||||
' Returns: Chuck Norris joke
|
||||
```
|
||||
|
||||
### Chuck Norris Categories
|
||||
```basic
|
||||
DESCRIPTION "Get available Chuck Norris joke categories"
|
||||
' Returns: Array of categories
|
||||
```
|
||||
|
||||
### Chuck Norris Joke by Category
|
||||
```basic
|
||||
PARAM category AS string LIKE "dev"
|
||||
' Returns: Joke from specific category
|
||||
```
|
||||
|
||||
### Dad Joke
|
||||
```basic
|
||||
DESCRIPTION "Get a random dad joke"
|
||||
' Returns: Dad joke from icanhazdadjoke
|
||||
```
|
||||
|
||||
### Search Dad Jokes
|
||||
```basic
|
||||
PARAM search_term AS string LIKE "cat"
|
||||
' Returns: Dad jokes containing search term
|
||||
```
|
||||
|
||||
### Bored Activity
|
||||
```basic
|
||||
DESCRIPTION "Get a random activity suggestion"
|
||||
' Returns: Activity suggestion with details
|
||||
```
|
||||
|
||||
### Bored Activity by Type
|
||||
```basic
|
||||
PARAM activity_type AS "education", "recreational", "social", "diy", "charity", "cooking", "relaxation", "music", "busywork"
|
||||
' Returns: Activity of specific type
|
||||
```
|
||||
|
||||
### Random Useless Fact
|
||||
```basic
|
||||
DESCRIPTION "Get a random useless but true fact"
|
||||
' Returns: Useless fact
|
||||
```
|
||||
|
||||
### Random Fun Fact
|
||||
```basic
|
||||
DESCRIPTION "Get a random fun fact"
|
||||
' Returns: Fun fact
|
||||
```
|
||||
|
||||
### Kanye West Quote
|
||||
```basic
|
||||
DESCRIPTION "Get a random Kanye West quote"
|
||||
' Returns: Kanye quote
|
||||
```
|
||||
|
||||
### Advice Slip
|
||||
```basic
|
||||
DESCRIPTION "Get a random piece of advice"
|
||||
' Returns: Random advice
|
||||
```
|
||||
|
||||
### Search Advice
|
||||
```basic
|
||||
PARAM query AS string LIKE "love"
|
||||
' Returns: Advice containing query word
|
||||
```
|
||||
|
||||
### Corporate Buzzword
|
||||
```basic
|
||||
DESCRIPTION "Get random corporate buzzwords"
|
||||
' Returns: Corporate buzzword phrase
|
||||
```
|
||||
|
||||
### Yo Momma Joke
|
||||
```basic
|
||||
DESCRIPTION "Get a random Yo Momma joke"
|
||||
' Returns: Yo Momma joke
|
||||
```
|
||||
|
||||
### Random Quote
|
||||
```basic
|
||||
DESCRIPTION "Get a random inspirational quote"
|
||||
' Returns: Quote with author
|
||||
```
|
||||
|
||||
### Quote by Author
|
||||
```basic
|
||||
PARAM author AS string LIKE "einstein"
|
||||
' Returns: Quote by specific author
|
||||
```
|
||||
|
||||
### Programming Quote
|
||||
```basic
|
||||
DESCRIPTION "Get a random programming quote"
|
||||
' Returns: Programming-related quote
|
||||
```
|
||||
|
||||
### Zen Quote
|
||||
```basic
|
||||
DESCRIPTION "Get a random Zen/Stoicism quote"
|
||||
' Returns: Zen quote
|
||||
```
|
||||
|
||||
### Affirmation
|
||||
```basic
|
||||
DESCRIPTION "Get a random positive affirmation"
|
||||
' Returns: Daily affirmation
|
||||
```
|
||||
|
||||
### Random Trivia
|
||||
```basic
|
||||
DESCRIPTION "Get a random trivia question"
|
||||
' Returns: Trivia question with answer
|
||||
```
|
||||
|
||||
### Multiple Trivia Questions
|
||||
```basic
|
||||
PARAM amount AS integer LIKE 5
|
||||
' Returns: Multiple trivia questions
|
||||
```
|
||||
|
||||
### Excuse Generator
|
||||
```basic
|
||||
DESCRIPTION "Get a random excuse"
|
||||
' Returns: Random excuse
|
||||
```
|
||||
|
||||
### Insult Generator
|
||||
```basic
|
||||
DESCRIPTION "Get a random insult (clean)"
|
||||
' Returns: Random insult
|
||||
```
|
||||
|
||||
### Compliment Generator
|
||||
```basic
|
||||
DESCRIPTION "Get a random compliment"
|
||||
' Returns: Random compliment
|
||||
```
|
||||
|
||||
## 🍽️ Food & Drink APIs
|
||||
|
||||
### Random Coffee Image
|
||||
```basic
|
||||
DESCRIPTION "Get a random coffee image"
|
||||
' Returns: Coffee image URL
|
||||
```
|
||||
|
||||
### Random Food Dish
|
||||
```basic
|
||||
DESCRIPTION "Get a random food dish image"
|
||||
' Returns: Food dish image
|
||||
```
|
||||
|
||||
### Random Food by Category
|
||||
```basic
|
||||
PARAM category AS "biryani", "burger", "butter-chicken", "dessert", "dosa", "idly", "pasta", "pizza", "rice", "samosa"
|
||||
' Returns: Food image from category
|
||||
```
|
||||
|
||||
### Random Meal Recipe
|
||||
```basic
|
||||
DESCRIPTION "Get a random meal recipe"
|
||||
' Returns: Full recipe with ingredients
|
||||
```
|
||||
|
||||
### Search Meal by Name
|
||||
```basic
|
||||
PARAM meal_name AS string LIKE "chicken"
|
||||
' Returns: Meals matching search
|
||||
```
|
||||
|
||||
### Random Cocktail Recipe
|
||||
```basic
|
||||
DESCRIPTION "Get a random cocktail recipe"
|
||||
' Returns: Cocktail recipe with ingredients
|
||||
```
|
||||
|
||||
### Search Cocktail by Name
|
||||
```basic
|
||||
PARAM cocktail_name AS string LIKE "margarita"
|
||||
' Returns: Cocktails matching search
|
||||
```
|
||||
|
||||
### Search Cocktail by Ingredient
|
||||
```basic
|
||||
PARAM ingredient AS string LIKE "vodka"
|
||||
' Returns: Cocktails with ingredient
|
||||
```
|
||||
|
||||
### Fruit Information
|
||||
```basic
|
||||
PARAM fruit_name AS string LIKE "apple"
|
||||
' Returns: Nutritional information
|
||||
```
|
||||
|
||||
### All Fruits List
|
||||
```basic
|
||||
DESCRIPTION "Get a list of all fruits"
|
||||
' Returns: Array of fruits
|
||||
```
|
||||
|
||||
### Fruits by Family
|
||||
```basic
|
||||
PARAM family AS string LIKE "Rosaceae"
|
||||
' Returns: Fruits from specific family
|
||||
```
|
||||
|
||||
### Random Taco Recipe
|
||||
```basic
|
||||
DESCRIPTION "Get a random taco recipe"
|
||||
' Returns: Taco recipe components
|
||||
```
|
||||
|
||||
### PunkAPI Beer Info
|
||||
```basic
|
||||
DESCRIPTION "Get a random beer recipe"
|
||||
' Returns: Beer details and recipe
|
||||
```
|
||||
|
||||
### Search Beer by Name
|
||||
```basic
|
||||
PARAM beer_name AS string LIKE "punk"
|
||||
' Returns: Beers matching search
|
||||
```
|
||||
|
||||
### High ABV Beers
|
||||
```basic
|
||||
PARAM min_abv AS number LIKE 8.0
|
||||
' Returns: Beers with high alcohol content
|
||||
```
|
||||
|
||||
### Bacon Ipsum Text
|
||||
```basic
|
||||
PARAM paragraphs AS integer LIKE 3
|
||||
' Returns: Bacon-themed lorem ipsum
|
||||
```
|
||||
|
||||
## 🔧 Data Utility & Geocoding APIs
|
||||
|
||||
### Generate UUID
|
||||
```basic
|
||||
DESCRIPTION "Generate a random UUID v4"
|
||||
' Returns: UUID string
|
||||
```
|
||||
|
||||
### Generate Multiple UUIDs
|
||||
```basic
|
||||
PARAM count AS integer LIKE 5
|
||||
' Returns: Array of UUIDs
|
||||
```
|
||||
|
||||
### Get My IP Address
|
||||
```basic
|
||||
DESCRIPTION "Get your current public IP"
|
||||
' Returns: IP address string
|
||||
```
|
||||
|
||||
### Get IP Geolocation
|
||||
```basic
|
||||
PARAM ip_address AS string LIKE "8.8.8.8"
|
||||
' Returns: Country, city, coordinates, ISP
|
||||
```
|
||||
|
||||
### Check if Number is Even
|
||||
```basic
|
||||
PARAM number AS integer LIKE 42
|
||||
' Returns: Boolean (humor API)
|
||||
```
|
||||
|
||||
### Random Data Generator
|
||||
```basic
|
||||
DESCRIPTION "Generate random test data"
|
||||
' Returns: User profile data
|
||||
```
|
||||
|
||||
### Generate Lorem Ipsum
|
||||
```basic
|
||||
PARAM paragraphs AS integer LIKE 3
|
||||
' Returns: Lorem ipsum text
|
||||
```
|
||||
|
||||
### QR Code Generator
|
||||
```basic
|
||||
PARAM text AS string LIKE "https://pragmatismo.com.br"
|
||||
PARAM size AS integer LIKE 200
|
||||
' Returns: QR code image
|
||||
```
|
||||
|
||||
### Barcode Generator
|
||||
```basic
|
||||
PARAM barcode_data AS string LIKE "1234567890"
|
||||
PARAM format AS "code128", "ean13", "upca", "code39"
|
||||
' Returns: Barcode image
|
||||
```
|
||||
|
||||
### Country Information
|
||||
```basic
|
||||
PARAM country AS string LIKE "brazil"
|
||||
' Returns: Detailed country data
|
||||
```
|
||||
|
||||
### All Countries List
|
||||
```basic
|
||||
DESCRIPTION "Get a list of all countries"
|
||||
' Returns: Array of 250+ countries
|
||||
```
|
||||
|
||||
### Countries by Region
|
||||
```basic
|
||||
PARAM region AS "africa", "americas", "asia", "europe", "oceania"
|
||||
' Returns: Countries in region
|
||||
```
|
||||
|
||||
### Currency Converter
|
||||
```basic
|
||||
PARAM amount AS number LIKE 100
|
||||
PARAM from_currency AS string LIKE "USD"
|
||||
PARAM to_currency AS string LIKE "EUR"
|
||||
' Returns: Converted amount
|
||||
```
|
||||
|
||||
### Timezone Info
|
||||
```basic
|
||||
PARAM timezone AS string LIKE "America/New_York"
|
||||
' Returns: Current time in timezone
|
||||
```
|
||||
|
||||
### All Timezones List
|
||||
```basic
|
||||
DESCRIPTION "Get all timezones"
|
||||
' Returns: Array of 400+ timezones
|
||||
```
|
||||
|
||||
### Public Holidays
|
||||
```basic
|
||||
PARAM country_code AS string LIKE "US"
|
||||
PARAM year AS integer LIKE 2024
|
||||
' Returns: List of public holidays
|
||||
```
|
||||
|
||||
### Number Facts
|
||||
```basic
|
||||
PARAM number AS integer LIKE 42
|
||||
' Returns: Interesting number fact
|
||||
```
|
||||
|
||||
### Random Number Fact
|
||||
```basic
|
||||
DESCRIPTION "Get a random number fact"
|
||||
' Returns: Random number fact
|
||||
```
|
||||
|
||||
### Date Facts
|
||||
```basic
|
||||
PARAM month AS integer LIKE 3
|
||||
PARAM day AS integer LIKE 14
|
||||
' Returns: Historical facts about date
|
||||
```
|
||||
|
||||
### Math Fact
|
||||
```basic
|
||||
PARAM number AS integer LIKE 1729
|
||||
' Returns: Mathematical fact
|
||||
```
|
||||
|
||||
### Yes or No Decision
|
||||
```basic
|
||||
DESCRIPTION "Get a random Yes/No answer"
|
||||
' Returns: Yes or No with GIF
|
||||
```
|
||||
|
||||
### Postcode Lookup UK
|
||||
```basic
|
||||
PARAM postcode AS string LIKE "SW1A1AA"
|
||||
' Returns: UK postcode information
|
||||
```
|
||||
|
||||
### Brazilian CEP Lookup
|
||||
```basic
|
||||
PARAM cep AS string LIKE "01310-100"
|
||||
' Returns: Brazilian postal code data
|
||||
```
|
||||
|
||||
### JSON Placeholder Post
|
||||
```basic
|
||||
DESCRIPTION "Get sample post data"
|
||||
' Returns: Test post data
|
||||
```
|
||||
|
||||
### Random User Generator
|
||||
```basic
|
||||
DESCRIPTION "Generate random user data"
|
||||
' Returns: Realistic user profile
|
||||
```
|
||||
|
||||
### Multiple Random Users
|
||||
```basic
|
||||
PARAM count AS integer LIKE 5
|
||||
' Returns: Array of user profiles
|
||||
```
|
||||
|
||||
## 🚀 Usage Examples
|
||||
|
||||
### Example 1: Weather Bot
|
||||
```basic
|
||||
TALK "Where would you like to check the weather?"
|
||||
HEAR city AS NAME
|
||||
|
||||
REM Get coordinates (you could use geocoding API)
|
||||
lat = 52.52
|
||||
lon = 13.41
|
||||
|
||||
REM Get weather data
|
||||
weather_url = "https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true"
|
||||
weather = GET weather_url
|
||||
|
||||
TALK "Current temperature in " + city + ": " + weather.current_weather.temperature + "°C"
|
||||
TALK "Wind speed: " + weather.current_weather.windspeed + " km/h"
|
||||
```
|
||||
|
||||
### Example 2: Daily Motivation Bot
|
||||
```basic
|
||||
REM Get random quote
|
||||
quote_data = GET "https://api.quotable.io/random"
|
||||
|
||||
REM Get affirmation
|
||||
affirmation = GET "https://www.affirmations.dev/"
|
||||
|
||||
TALK "🌟 Daily Motivation:"
|
||||
TALK ""
|
||||
TALK "Quote of the Day:"
|
||||
TALK '"' + quote_data.content + '"'
|
||||
TALK "— " + quote_data.author
|
||||
TALK ""
|
||||
TALK "💖 Affirmation:"
|
||||
TALK affirmation.affirmation
|
||||
```
|
||||
|
||||
### Example 3: Random Pet Image Bot
|
||||
```basic
|
||||
HEAR choice AS "Cat", "Dog", "Fox", "Duck"
|
||||
|
||||
IF choice = "Cat" THEN
|
||||
image_url = "https://cataas.com/cat"
|
||||
ELSE IF choice = "Dog" THEN
|
||||
dog_data = GET "https://random.dog/woof.json"
|
||||
image_url = dog_data.url
|
||||
ELSE IF choice = "Fox" THEN
|
||||
fox_data = GET "https://randomfox.ca/floof/"
|
||||
image_url = fox_data.image
|
||||
ELSE IF choice = "Duck" THEN
|
||||
duck_data = GET "https://random-d.uk/api/random"
|
||||
image_url = duck_data.url
|
||||
END IF
|
||||
|
||||
TALK "Here's your random " + choice + " image!"
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
```
|
||||
|
||||
### Example 4: Recipe Finder Bot
|
||||
```basic
|
||||
TALK "What are you in the mood for?"
|
||||
HEAR food AS "Meal", "Cocktail", "Beer"
|
||||
|
||||
IF food = "Meal" THEN
|
||||
meal = GET "https://www.themealdb.com/api/json/v1/1/random.php"
|
||||
recipe = meal.meals[0]
|
||||
TALK "🍳 " + recipe.strMeal
|
||||
TALK recipe.strInstructions
|
||||
|
||||
ELSE IF food = "Cocktail" THEN
|
||||
cocktail = GET "https://www.thecocktaildb.com/api/json/v1/1/random.php"
|
||||
drink = cocktail.drinks[0]
|
||||
TALK "🍹 " + drink.strDrink
|
||||
TALK drink.strInstructions
|
||||
|
||||
ELSE IF food = "Beer" THEN
|
||||
beer_data = GET "https://api.punkapi.com/v2/beers/random"
|
||||
beer = beer_data[0]
|
||||
TALK "🍺 " + beer.name
|
||||
TALK beer.description
|
||||
END IF
|
||||
```
|
||||
|
||||
### Example 5: Travel Information Bot
|
||||
```basic
|
||||
TALK "Which country would you like to know about?"
|
||||
HEAR country AS NAME
|
||||
|
||||
country_url = "https://restcountries.com/v3.1/name/" + country
|
||||
country_data = GET country_url
|
||||
|
||||
IF country_data AND UBOUND(country_data) > 0 THEN
|
||||
info = country_data[0]
|
||||
|
||||
TALK "🌍 " + info.name.common
|
||||
TALK "Capital: " + info.capital[0]
|
||||
TALK "Population: " + info.population
|
||||
TALK "Region: " + info.region
|
||||
TALK "Languages: " + JOIN(info.languages)
|
||||
TALK "Currency: " + JOIN(info.currencies)
|
||||
|
||||
REM Get public holidays
|
||||
holidays_url = "https://date.nager.at/api/v3/PublicHolidays/2024/" + info.cca2
|
||||
holidays = GET holidays_url
|
||||
|
||||
TALK ""
|
||||
TALK "🎉 Upcoming Holidays:"
|
||||
FOR EACH holiday IN holidays
|
||||
TALK "• " + holiday.date + " - " + holiday.name
|
||||
END FOR
|
||||
END IF
|
||||
```
|
||||
|
||||
## 📚 API Sources
|
||||
|
||||
All APIs in this package are from the [public-apis](https://github.com/public-apis/public-apis) repository and require no authentication.
|
||||
|
||||
### Categories Covered:
|
||||
- ☁️ Weather & Environment
|
||||
- 🐾 Animals & Pets
|
||||
- 😄 Entertainment & Humor
|
||||
- 🍽️ Food & Drink
|
||||
- 🌍 Geography & Location
|
||||
- 📊 Data & Utilities
|
||||
- 💱 Currency & Finance
|
||||
- 🎲 Random Generators
|
||||
- 📚 Facts & Trivia
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
1. **No API Keys Required**: All keywords use free, no-auth APIs
|
||||
2. **Rate Limits**: Some APIs may have rate limits on free tier
|
||||
3. **Availability**: APIs are third-party services and availability may vary
|
||||
4. **Production Use**: For production apps, consider APIs with authentication for better reliability
|
||||
5. **Terms of Service**: Always respect the terms of service of each API
|
||||
|
||||
## 🔧 Customization
|
||||
|
||||
You can easily extend these keywords or create your own:
|
||||
|
||||
```basic
|
||||
REM Template for new API keyword
|
||||
PARAM your_param AS string LIKE "example"
|
||||
DESCRIPTION "What your keyword does"
|
||||
|
||||
api_url = "https://api.example.com/endpoint?param=" + your_param
|
||||
data = GET api_url
|
||||
|
||||
IF data THEN
|
||||
TALK "Success!"
|
||||
TALK data.result
|
||||
RETURN data
|
||||
ELSE
|
||||
TALK "❌ Error fetching data"
|
||||
RETURN NULL
|
||||
END IF
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
To add more API keywords:
|
||||
1. Find a free, no-auth API from [public-apis](https://github.com/public-apis/public-apis)
|
||||
2. Create a `.bas` or `.bas` file in the appropriate category
|
||||
3. Follow the existing keyword pattern
|
||||
4. Test thoroughly
|
||||
5. Update this README
|
||||
|
||||
## 📄 License
|
||||
|
||||
This template follows the General Bots license. Individual APIs have their own terms of service.
|
||||
|
||||
## 🌟 Credits
|
||||
|
||||
- [public-apis](https://github.com/public-apis/public-apis) - Comprehensive list of public APIs
|
||||
- [7Timer!](http://www.7timer.info/) - Weather forecasting
|
||||
- [Open-Meteo](https://open-meteo.com/) - Weather API
|
||||
- [TheMealDB](https://www.themealdb.com/) - Meal recipes
|
||||
- [TheCocktailDB](https://www.thecocktaildb.com/) - Cocktail recipes
|
||||
- And many more amazing free API providers!
|
||||
|
||||
---
|
||||
|
||||
**General Bots**: Your Prompt Engineering Gets Done. 🤖
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
REM General Bots: Animals & Pets APIs - Free Animal Data Integration
|
||||
REM Based on public-apis list - No authentication required
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Cat Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random cat fact"
|
||||
|
||||
cat_fact = GET "https://catfact.ninja/fact"
|
||||
|
||||
TALK "🐱 Random Cat Fact:"
|
||||
TALK cat_fact.fact
|
||||
|
||||
RETURN cat_fact.fact
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Dog Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random dog fact"
|
||||
|
||||
dog_fact = GET "https://dogapi.dog/api/v2/facts"
|
||||
|
||||
IF dog_fact.data AND UBOUND(dog_fact.data) > 0 THEN
|
||||
fact_text = dog_fact.data[0].attributes.body
|
||||
|
||||
TALK "🐶 Random Dog Fact:"
|
||||
TALK fact_text
|
||||
|
||||
RETURN fact_text
|
||||
ELSE
|
||||
TALK "❌ Could not fetch dog fact"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Dog Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random dog image URL"
|
||||
|
||||
dog_image = GET "https://random.dog/woof.json"
|
||||
|
||||
image_url = dog_image.url
|
||||
|
||||
TALK "🐕 Random Dog Image:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Cat Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random cat image from Cataas"
|
||||
|
||||
cat_url = "https://cataas.com/cat"
|
||||
|
||||
TALK "🐈 Random Cat Image:"
|
||||
TALK cat_url
|
||||
|
||||
file = DOWNLOAD cat_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN cat_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Fox Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random fox image"
|
||||
|
||||
fox_data = GET "https://randomfox.ca/floof/"
|
||||
|
||||
image_url = fox_data.image
|
||||
|
||||
TALK "🦊 Random Fox Image:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Duck Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random duck image"
|
||||
|
||||
duck_url = "https://random-d.uk/api/random"
|
||||
duck_data = GET duck_url
|
||||
|
||||
image_url = duck_data.url
|
||||
message = duck_data.message
|
||||
|
||||
TALK "🦆 Random Duck Image:"
|
||||
TALK message
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Shiba Inu Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Shiba Inu dog image"
|
||||
|
||||
shiba_data = GET "https://shibe.online/api/shibes?count=1"
|
||||
|
||||
IF UBOUND(shiba_data) > 0 THEN
|
||||
image_url = shiba_data[0]
|
||||
|
||||
TALK "🐕 Random Shiba Inu Image:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
ELSE
|
||||
TALK "❌ Could not fetch Shiba image"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - HTTP Cat (HTTP Status Cats)
|
||||
REM ============================================
|
||||
PARAM status_code AS integer LIKE 404
|
||||
DESCRIPTION "Get a cat image representing an HTTP status code"
|
||||
|
||||
cat_url = "https://http.cat/" + status_code
|
||||
|
||||
TALK "🐱 HTTP Cat for status " + status_code + ":"
|
||||
TALK cat_url
|
||||
|
||||
file = DOWNLOAD cat_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN cat_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - HTTP Dog (HTTP Status Dogs)
|
||||
REM ============================================
|
||||
PARAM status_code AS integer LIKE 404
|
||||
DESCRIPTION "Get a dog image representing an HTTP status code"
|
||||
|
||||
dog_url = "https://httpstatusdogs.com/img/" + status_code + ".jpg"
|
||||
|
||||
TALK "🐶 HTTP Dog for status " + status_code + ":"
|
||||
TALK dog_url
|
||||
|
||||
file = DOWNLOAD dog_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN dog_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - PlaceBear Placeholder
|
||||
REM ============================================
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
DESCRIPTION "Get a placeholder bear image of specified dimensions"
|
||||
|
||||
bear_url = "https://placebear.com/" + width + "/" + height
|
||||
|
||||
TALK "🐻 Bear Placeholder Image (" + width + "x" + height + "):"
|
||||
TALK bear_url
|
||||
|
||||
file = DOWNLOAD bear_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN bear_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - PlaceDog Placeholder
|
||||
REM ============================================
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
DESCRIPTION "Get a placeholder dog image of specified dimensions"
|
||||
|
||||
dog_url = "https://placedog.net/" + width + "/" + height
|
||||
|
||||
TALK "🐕 Dog Placeholder Image (" + width + "x" + height + "):"
|
||||
TALK dog_url
|
||||
|
||||
file = DOWNLOAD dog_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN dog_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - PlaceKitten Placeholder
|
||||
REM ============================================
|
||||
PARAM width AS integer LIKE 400
|
||||
PARAM height AS integer LIKE 300
|
||||
DESCRIPTION "Get a placeholder kitten image of specified dimensions"
|
||||
|
||||
kitten_url = "https://placekitten.com/" + width + "/" + height
|
||||
|
||||
TALK "🐱 Kitten Placeholder Image (" + width + "x" + height + "):"
|
||||
TALK kitten_url
|
||||
|
||||
file = DOWNLOAD kitten_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN kitten_url
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - MeowFacts
|
||||
REM ============================================
|
||||
PARAM count AS integer LIKE 1
|
||||
DESCRIPTION "Get random cat facts (up to 100)"
|
||||
|
||||
facts_url = "https://meowfacts.herokuapp.com/?count=" + count
|
||||
|
||||
meow_data = GET facts_url
|
||||
|
||||
TALK "🐱 Random Cat Facts:"
|
||||
|
||||
FOR EACH fact IN meow_data.data
|
||||
TALK "• " + fact
|
||||
END FOR
|
||||
|
||||
RETURN meow_data.data
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Random Axolotl
|
||||
REM ============================================
|
||||
DESCRIPTION "Get random axolotl picture and facts"
|
||||
|
||||
axolotl_data = GET "https://theaxolotlapi.netlify.app/.netlify/functions/axolotl"
|
||||
|
||||
image_url = axolotl_data.url
|
||||
facts = axolotl_data.facts
|
||||
|
||||
TALK "🦎 Random Axolotl:"
|
||||
TALK image_url
|
||||
|
||||
IF facts THEN
|
||||
TALK ""
|
||||
TALK "📚 Axolotl Facts:"
|
||||
FOR EACH fact IN facts
|
||||
TALK "• " + fact
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN axolotl_data
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Zoo Animals Info
|
||||
REM ============================================
|
||||
DESCRIPTION "Get information about various zoo animals"
|
||||
|
||||
zoo_data = GET "https://zoo-animal-api.herokuapp.com/animals/rand"
|
||||
|
||||
name = zoo_data.name
|
||||
latin_name = zoo_data.latin_name
|
||||
animal_type = zoo_data.animal_type
|
||||
habitat = zoo_data.habitat
|
||||
lifespan = zoo_data.lifespan
|
||||
diet = zoo_data.diet
|
||||
image_url = zoo_data.image_link
|
||||
|
||||
TALK "🦁 Random Zoo Animal: " + name
|
||||
TALK "🔬 Latin Name: " + latin_name
|
||||
TALK "📦 Type: " + animal_type
|
||||
TALK "🏡 Habitat: " + habitat
|
||||
TALK "⏳ Lifespan: " + lifespan
|
||||
TALK "🍖 Diet: " + diet
|
||||
TALK "📷 Image: " + image_url
|
||||
|
||||
IF image_url THEN
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
END IF
|
||||
|
||||
RETURN zoo_data
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Multiple Random Dogs
|
||||
REM ============================================
|
||||
PARAM count AS integer LIKE 3
|
||||
DESCRIPTION "Get multiple random dog images"
|
||||
|
||||
dog_url = "https://dog.ceo/api/breeds/image/random/" + count
|
||||
|
||||
dog_data = GET dog_url
|
||||
|
||||
IF dog_data.status = "success" THEN
|
||||
TALK "🐕 " + count + " Random Dog Images:"
|
||||
|
||||
FOR EACH image IN dog_data.message
|
||||
TALK image
|
||||
file = DOWNLOAD image
|
||||
SEND FILE file
|
||||
WAIT 1
|
||||
END FOR
|
||||
|
||||
RETURN dog_data.message
|
||||
ELSE
|
||||
TALK "❌ Could not fetch dog images"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Dog Breeds List
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a list of all dog breeds"
|
||||
|
||||
breeds_url = "https://dog.ceo/api/breeds/list/all"
|
||||
|
||||
breeds_data = GET breeds_url
|
||||
|
||||
IF breeds_data.status = "success" THEN
|
||||
breed_count = 0
|
||||
breed_list = NEW ARRAY
|
||||
|
||||
TALK "🐕 Available Dog Breeds:"
|
||||
|
||||
FOR EACH breed IN breeds_data.message
|
||||
breed_count = breed_count + 1
|
||||
breed_list.PUSH(breed)
|
||||
IF breed_count <= 20 THEN
|
||||
TALK "• " + breed
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
IF breed_count > 20 THEN
|
||||
TALK "... and " + (breed_count - 20) + " more breeds"
|
||||
END IF
|
||||
|
||||
RETURN breed_list
|
||||
ELSE
|
||||
TALK "❌ Could not fetch breed list"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ANIMAL KEYWORD - Specific Dog Breed Image
|
||||
REM ============================================
|
||||
PARAM breed AS string LIKE "husky"
|
||||
DESCRIPTION "Get a random image of a specific dog breed"
|
||||
|
||||
breed_url = "https://dog.ceo/api/breed/" + breed + "/images/random"
|
||||
|
||||
breed_data = GET breed_url
|
||||
|
||||
IF breed_data.status = "success" THEN
|
||||
image_url = breed_data.message
|
||||
|
||||
TALK "🐕 Random " + breed + " image:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
ELSE
|
||||
TALK "❌ Breed not found: " + breed
|
||||
TALK "Use 'Dog Breeds List' keyword to see available breeds"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
|
@ -0,0 +1,568 @@
|
|||
REM General Bots: Data Utility & Geocoding APIs - Free Data Services
|
||||
REM Based on public-apis list - No authentication required
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Generate UUID
|
||||
REM ============================================
|
||||
DESCRIPTION "Generate a random UUID v4"
|
||||
|
||||
uuid_data = GET "https://www.uuidgenerator.net/api/version4"
|
||||
|
||||
TALK "🔑 Generated UUID:"
|
||||
TALK uuid_data
|
||||
|
||||
RETURN uuid_data
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Generate Multiple UUIDs
|
||||
REM ============================================
|
||||
PARAM count AS integer LIKE 5
|
||||
DESCRIPTION "Generate multiple UUIDs"
|
||||
|
||||
TALK "🔑 Generated " + count + " UUIDs:"
|
||||
|
||||
uuids = NEW ARRAY
|
||||
|
||||
FOR i = 1 TO count
|
||||
uuid = GET "https://www.uuidgenerator.net/api/version4"
|
||||
uuids.PUSH(uuid)
|
||||
TALK i + ". " + uuid
|
||||
NEXT i
|
||||
|
||||
RETURN uuids
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Get My IP Address
|
||||
REM ============================================
|
||||
DESCRIPTION "Get your current public IP address"
|
||||
|
||||
ip_data = GET "https://api.ipify.org?format=json"
|
||||
|
||||
TALK "🌐 Your Public IP Address:"
|
||||
TALK ip_data.ip
|
||||
|
||||
RETURN ip_data.ip
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Get IP Geolocation
|
||||
REM ============================================
|
||||
PARAM ip_address AS string LIKE "8.8.8.8"
|
||||
DESCRIPTION "Get geolocation information for an IP address"
|
||||
|
||||
geo_url = "http://ip-api.com/json/" + ip_address
|
||||
|
||||
geo_data = GET geo_url
|
||||
|
||||
IF geo_data.status = "success" THEN
|
||||
TALK "🌍 IP Geolocation for " + ip_address + ":"
|
||||
TALK "📍 Country: " + geo_data.country + " (" + geo_data.countryCode + ")"
|
||||
TALK "🏙️ City: " + geo_data.city
|
||||
TALK "📮 ZIP Code: " + geo_data.zip
|
||||
TALK "🗺️ Coordinates: " + geo_data.lat + ", " + geo_data.lon
|
||||
TALK "⏰ Timezone: " + geo_data.timezone
|
||||
TALK "🏢 ISP: " + geo_data.isp
|
||||
TALK "🏛️ Organization: " + geo_data.org
|
||||
|
||||
RETURN geo_data
|
||||
ELSE
|
||||
TALK "❌ Could not get geolocation for IP: " + ip_address
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Check if Number is Even
|
||||
REM ============================================
|
||||
PARAM number AS integer LIKE 42
|
||||
DESCRIPTION "Check if a number is even (humor API)"
|
||||
|
||||
even_data = GET "https://api.isevenapi.xyz/api/iseven/" + number
|
||||
|
||||
TALK "🔢 Is " + number + " even?"
|
||||
TALK even_data.iseven
|
||||
|
||||
IF even_data.iseven = TRUE THEN
|
||||
TALK "✅ Yes, " + number + " is even!"
|
||||
ELSE
|
||||
TALK "❌ No, " + number + " is odd!"
|
||||
END IF
|
||||
|
||||
RETURN even_data.iseven
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Random Data Generator
|
||||
REM ============================================
|
||||
DESCRIPTION "Generate random test data (name, address, etc)"
|
||||
|
||||
random_data = GET "https://random-data-api.com/api/v2/users"
|
||||
|
||||
TALK "👤 Random User Data:"
|
||||
TALK "Name: " + random_data.first_name + " " + random_data.last_name
|
||||
TALK "Username: " + random_data.username
|
||||
TALK "Email: " + random_data.email
|
||||
TALK "Phone: " + random_data.phone_number
|
||||
TALK "Date of Birth: " + random_data.date_of_birth
|
||||
TALK "Address: " + random_data.address.street_address
|
||||
TALK "City: " + random_data.address.city
|
||||
TALK "State: " + random_data.address.state
|
||||
TALK "Country: " + random_data.address.country
|
||||
|
||||
RETURN random_data
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Generate Lorem Ipsum
|
||||
REM ============================================
|
||||
PARAM paragraphs AS integer LIKE 3
|
||||
DESCRIPTION "Generate Lorem Ipsum placeholder text"
|
||||
|
||||
lorem_url = "https://loripsum.net/api/" + paragraphs + "/medium/plaintext"
|
||||
|
||||
lorem_text = GET lorem_url
|
||||
|
||||
TALK "📝 Lorem Ipsum Text (" + paragraphs + " paragraphs):"
|
||||
TALK lorem_text
|
||||
|
||||
RETURN lorem_text
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - QR Code Generator
|
||||
REM ============================================
|
||||
PARAM text AS string LIKE "https://pragmatismo.com.br"
|
||||
PARAM size AS integer LIKE 200
|
||||
DESCRIPTION "Generate a QR code for any text or URL"
|
||||
|
||||
qr_url = "https://api.qrserver.com/v1/create-qr-code/?size=" + size + "x" + size + "&data=" + text
|
||||
|
||||
TALK "📱 QR Code generated for:"
|
||||
TALK text
|
||||
TALK ""
|
||||
TALK "🔗 QR Code URL:"
|
||||
TALK qr_url
|
||||
|
||||
file = DOWNLOAD qr_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN qr_url
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Barcode Generator
|
||||
REM ============================================
|
||||
PARAM barcode_data AS string LIKE "1234567890"
|
||||
PARAM format AS "code128", "ean13", "upca", "code39"
|
||||
DESCRIPTION "Generate a barcode image"
|
||||
|
||||
barcode_url = "https://barcodeapi.org/api/" + format + "/" + barcode_data
|
||||
|
||||
TALK "📊 Barcode generated:"
|
||||
TALK "Format: " + format
|
||||
TALK "Data: " + barcode_data
|
||||
TALK ""
|
||||
TALK "🔗 Barcode URL:"
|
||||
TALK barcode_url
|
||||
|
||||
file = DOWNLOAD barcode_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN barcode_url
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Country Information
|
||||
REM ============================================
|
||||
PARAM country AS string LIKE "brazil"
|
||||
DESCRIPTION "Get detailed information about a country"
|
||||
|
||||
country_url = "https://restcountries.com/v3.1/name/" + country
|
||||
|
||||
country_data = GET country_url
|
||||
|
||||
IF country_data AND UBOUND(country_data) > 0 THEN
|
||||
info = country_data[0]
|
||||
|
||||
TALK "🌍 Country Information: " + info.name.common
|
||||
TALK "🏛️ Official Name: " + info.name.official
|
||||
TALK "🏳️ Capital: " + info.capital[0]
|
||||
TALK "🗺️ Region: " + info.region + " (" + info.subregion + ")"
|
||||
TALK "👥 Population: " + info.population
|
||||
TALK "📏 Area: " + info.area + " km²"
|
||||
TALK "🌐 Languages: " + JOIN(info.languages)
|
||||
TALK "💰 Currencies: " + JOIN(info.currencies)
|
||||
TALK "⏰ Timezones: " + JOIN(info.timezones)
|
||||
TALK "🚗 Drives on: " + info.car.side
|
||||
TALK "🌐 Top Level Domain: " + info.tld[0]
|
||||
|
||||
IF info.flags.png THEN
|
||||
TALK ""
|
||||
TALK "🏴 Flag:"
|
||||
file = DOWNLOAD info.flags.png
|
||||
SEND FILE file
|
||||
END IF
|
||||
|
||||
RETURN info
|
||||
ELSE
|
||||
TALK "❌ Country not found: " + country
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - All Countries List
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a list of all countries"
|
||||
|
||||
countries = GET "https://restcountries.com/v3.1/all"
|
||||
|
||||
TALK "🌍 Total Countries: " + UBOUND(countries)
|
||||
TALK ""
|
||||
TALK "First 20 countries:"
|
||||
|
||||
counter = 0
|
||||
FOR EACH country IN countries
|
||||
IF counter < 20 THEN
|
||||
TALK "• " + country.name.common + " (" + country.cca2 + ")"
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 20 THEN
|
||||
TALK "... and " + (counter - 20) + " more countries"
|
||||
END IF
|
||||
|
||||
RETURN countries
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Countries by Region
|
||||
REM ============================================
|
||||
PARAM region AS "africa", "americas", "asia", "europe", "oceania"
|
||||
DESCRIPTION "Get countries from a specific region"
|
||||
|
||||
region_url = "https://restcountries.com/v3.1/region/" + region
|
||||
|
||||
countries = GET region_url
|
||||
|
||||
TALK "🌍 Countries in " + region + ":"
|
||||
TALK "Total: " + UBOUND(countries)
|
||||
TALK ""
|
||||
|
||||
FOR EACH country IN countries
|
||||
TALK "• " + country.name.common + " - Capital: " + country.capital[0]
|
||||
END FOR
|
||||
|
||||
RETURN countries
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Currency Converter
|
||||
REM ============================================
|
||||
PARAM amount AS number LIKE 100
|
||||
PARAM from_currency AS string LIKE "USD"
|
||||
PARAM to_currency AS string LIKE "EUR"
|
||||
DESCRIPTION "Convert currency amounts (Note: Free tier available)"
|
||||
|
||||
exchange_url = "https://api.exchangerate-api.com/v4/latest/" + from_currency
|
||||
|
||||
exchange_data = GET exchange_url
|
||||
|
||||
IF exchange_data.rates THEN
|
||||
rate = exchange_data.rates[to_currency]
|
||||
converted = amount * rate
|
||||
|
||||
TALK "💱 Currency Conversion:"
|
||||
TALK amount + " " + from_currency + " = " + converted + " " + to_currency
|
||||
TALK "Exchange Rate: 1 " + from_currency + " = " + rate + " " + to_currency
|
||||
TALK "Updated: " + exchange_data.date
|
||||
|
||||
result = NEW OBJECT
|
||||
result.amount = amount
|
||||
result.from = from_currency
|
||||
result.to = to_currency
|
||||
result.rate = rate
|
||||
result.converted = converted
|
||||
|
||||
RETURN result
|
||||
ELSE
|
||||
TALK "❌ Could not fetch exchange rates"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Timezone Info
|
||||
REM ============================================
|
||||
PARAM timezone AS string LIKE "America/New_York"
|
||||
DESCRIPTION "Get current time in a specific timezone"
|
||||
|
||||
time_url = "http://worldtimeapi.org/api/timezone/" + timezone
|
||||
|
||||
time_data = GET time_url
|
||||
|
||||
IF time_data.datetime THEN
|
||||
TALK "⏰ Current Time in " + timezone + ":"
|
||||
TALK "🕐 DateTime: " + time_data.datetime
|
||||
TALK "📅 Date: " + time_data.date
|
||||
TALK "⏲️ Time: " + time_data.time
|
||||
TALK "🌍 UTC Offset: " + time_data.utc_offset
|
||||
TALK "📆 Day of Week: " + time_data.day_of_week
|
||||
TALK "📆 Day of Year: " + time_data.day_of_year
|
||||
TALK "📆 Week Number: " + time_data.week_number
|
||||
|
||||
RETURN time_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch timezone data for: " + timezone
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - All Timezones List
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a list of all available timezones"
|
||||
|
||||
timezones = GET "http://worldtimeapi.org/api/timezone"
|
||||
|
||||
TALK "🌍 Available Timezones (" + UBOUND(timezones) + " total):"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH tz IN timezones
|
||||
IF counter < 30 THEN
|
||||
TALK "• " + tz
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 30 THEN
|
||||
TALK "... and " + (counter - 30) + " more timezones"
|
||||
END IF
|
||||
|
||||
RETURN timezones
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Public Holidays
|
||||
REM ============================================
|
||||
PARAM country_code AS string LIKE "US"
|
||||
PARAM year AS integer LIKE 2024
|
||||
DESCRIPTION "Get public holidays for a country and year"
|
||||
|
||||
holidays_url = "https://date.nager.at/api/v3/PublicHolidays/" + year + "/" + country_code
|
||||
|
||||
holidays = GET holidays_url
|
||||
|
||||
IF holidays THEN
|
||||
TALK "🎉 Public Holidays in " + country_code + " for " + year + ":"
|
||||
TALK "Total: " + UBOUND(holidays)
|
||||
TALK ""
|
||||
|
||||
FOR EACH holiday IN holidays
|
||||
TALK "📅 " + holiday.date + " - " + holiday.name
|
||||
IF holiday.localName <> holiday.name THEN
|
||||
TALK " (" + holiday.localName + ")"
|
||||
END IF
|
||||
END FOR
|
||||
|
||||
RETURN holidays
|
||||
ELSE
|
||||
TALK "❌ Could not fetch holidays for: " + country_code
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Number Facts
|
||||
REM ============================================
|
||||
PARAM number AS integer LIKE 42
|
||||
DESCRIPTION "Get an interesting fact about a number"
|
||||
|
||||
fact_url = "http://numbersapi.com/" + number
|
||||
|
||||
number_fact = GET fact_url
|
||||
|
||||
TALK "🔢 Fact about " + number + ":"
|
||||
TALK number_fact
|
||||
|
||||
RETURN number_fact
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Random Number Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random number fact"
|
||||
|
||||
random_fact = GET "http://numbersapi.com/random"
|
||||
|
||||
TALK "🔢 Random Number Fact:"
|
||||
TALK random_fact
|
||||
|
||||
RETURN random_fact
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Date Facts
|
||||
REM ============================================
|
||||
PARAM month AS integer LIKE 3
|
||||
PARAM day AS integer LIKE 14
|
||||
DESCRIPTION "Get interesting facts about a specific date"
|
||||
|
||||
date_url = "http://numbersapi.com/" + month + "/" + day + "/date"
|
||||
|
||||
date_fact = GET date_url
|
||||
|
||||
TALK "📅 Fact about " + month + "/" + day + ":"
|
||||
TALK date_fact
|
||||
|
||||
RETURN date_fact
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Math Fact
|
||||
REM ============================================
|
||||
PARAM number AS integer LIKE 1729
|
||||
DESCRIPTION "Get a mathematical fact about a number"
|
||||
|
||||
math_url = "http://numbersapi.com/" + number + "/math"
|
||||
|
||||
math_fact = GET math_url
|
||||
|
||||
TALK "🧮 Math Fact about " + number + ":"
|
||||
TALK math_fact
|
||||
|
||||
RETURN math_fact
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Yes or No Decision
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Yes or No answer"
|
||||
|
||||
decision = GET "https://yesno.wtf/api"
|
||||
|
||||
answer = decision.answer
|
||||
image = decision.image
|
||||
|
||||
TALK "🎲 Random Decision:"
|
||||
TALK UCASE(answer) + "!"
|
||||
|
||||
file = DOWNLOAD image
|
||||
SEND FILE file
|
||||
|
||||
RETURN decision
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Postcode Lookup UK
|
||||
REM ============================================
|
||||
PARAM postcode AS string LIKE "SW1A1AA"
|
||||
DESCRIPTION "Look up UK postcode information"
|
||||
|
||||
postcode_clean = REPLACE(postcode, " ", "")
|
||||
postcode_url = "https://api.postcodes.io/postcodes/" + postcode_clean
|
||||
|
||||
postcode_data = GET postcode_url
|
||||
|
||||
IF postcode_data.status = 200 THEN
|
||||
result = postcode_data.result
|
||||
|
||||
TALK "📮 UK Postcode Information:"
|
||||
TALK "Postcode: " + result.postcode
|
||||
TALK "🏙️ Region: " + result.region
|
||||
TALK "🗺️ District: " + result.admin_district
|
||||
TALK "📍 Coordinates: " + result.latitude + ", " + result.longitude
|
||||
TALK "🏛️ Parliamentary Constituency: " + result.parliamentary_constituency
|
||||
TALK "🌍 Country: " + result.country
|
||||
|
||||
RETURN result
|
||||
ELSE
|
||||
TALK "❌ Invalid postcode: " + postcode
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Brazilian CEP Lookup
|
||||
REM ============================================
|
||||
PARAM cep AS string LIKE "01310-100"
|
||||
DESCRIPTION "Look up Brazilian postal code (CEP) information"
|
||||
|
||||
cep_clean = REPLACE(cep, "-", "")
|
||||
cep_url = "https://viacep.com.br/ws/" + cep_clean + "/json/"
|
||||
|
||||
cep_data = GET cep_url
|
||||
|
||||
IF NOT cep_data.erro THEN
|
||||
TALK "📮 CEP Information:"
|
||||
TALK "CEP: " + cep_data.cep
|
||||
TALK "🏙️ City: " + cep_data.localidade + " - " + cep_data.uf
|
||||
TALK "🏘️ Neighborhood: " + cep_data.bairro
|
||||
TALK "🛣️ Street: " + cep_data.logradouro
|
||||
TALK "🗺️ Region: " + cep_data.regiao
|
||||
TALK "☎️ DDD: " + cep_data.ddd
|
||||
|
||||
RETURN cep_data
|
||||
ELSE
|
||||
TALK "❌ Invalid CEP: " + cep
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - JSON Placeholder Post
|
||||
REM ============================================
|
||||
DESCRIPTION "Get sample post data for testing"
|
||||
|
||||
post = GET "https://jsonplaceholder.typicode.com/posts/1"
|
||||
|
||||
TALK "📝 Sample Post Data:"
|
||||
TALK "Title: " + post.title
|
||||
TALK "User ID: " + post.userId
|
||||
TALK "Post ID: " + post.id
|
||||
TALK ""
|
||||
TALK "Body:"
|
||||
TALK post.body
|
||||
|
||||
RETURN post
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Random User Generator
|
||||
REM ============================================
|
||||
DESCRIPTION "Generate a random user with realistic data"
|
||||
|
||||
user_data = GET "https://randomuser.me/api/"
|
||||
|
||||
IF user_data.results AND UBOUND(user_data.results) > 0 THEN
|
||||
user = user_data.results[0]
|
||||
|
||||
TALK "👤 Random User Generated:"
|
||||
TALK "Name: " + user.name.first + " " + user.name.last
|
||||
TALK "Gender: " + user.gender
|
||||
TALK "📧 Email: " + user.email
|
||||
TALK "📱 Phone: " + user.phone
|
||||
TALK "🎂 Date of Birth: " + user.dob.date
|
||||
TALK "📍 Location: " + user.location.city + ", " + user.location.state + ", " + user.location.country
|
||||
TALK "📮 Postcode: " + user.location.postcode
|
||||
TALK "🌐 Nationality: " + user.nat
|
||||
TALK ""
|
||||
TALK "📷 Picture: " + user.picture.large
|
||||
|
||||
file = DOWNLOAD user.picture.large
|
||||
SEND FILE file
|
||||
|
||||
RETURN user
|
||||
ELSE
|
||||
TALK "❌ Could not generate random user"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM DATA KEYWORD - Multiple Random Users
|
||||
REM ============================================
|
||||
PARAM count AS integer LIKE 5
|
||||
DESCRIPTION "Generate multiple random users"
|
||||
|
||||
users_url = "https://randomuser.me/api/?results=" + count
|
||||
|
||||
users_data = GET users_url
|
||||
|
||||
IF users_data.results THEN
|
||||
TALK "👥 Generated " + count + " Random Users:"
|
||||
TALK ""
|
||||
|
||||
counter = 1
|
||||
FOR EACH user IN users_data.results
|
||||
TALK counter + ". " + user.name.first + " " + user.name.last
|
||||
TALK " Email: " + user.email
|
||||
TALK " Location: " + user.location.city + ", " + user.location.country
|
||||
TALK ""
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
RETURN users_data.results
|
||||
ELSE
|
||||
TALK "❌ Could not generate random users"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
REM General Bots: Entertainment APIs - Jokes, Quotes, and Fun Content
|
||||
REM Based on public-apis list - No authentication required
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Chuck Norris Joke
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Chuck Norris joke"
|
||||
|
||||
chuck_joke = GET "https://api.chucknorris.io/jokes/random"
|
||||
|
||||
TALK "😄 Chuck Norris Joke:"
|
||||
TALK chuck_joke.value
|
||||
|
||||
RETURN chuck_joke.value
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Chuck Norris Categories
|
||||
REM ============================================
|
||||
DESCRIPTION "Get available Chuck Norris joke categories"
|
||||
|
||||
categories = GET "https://api.chucknorris.io/jokes/categories"
|
||||
|
||||
TALK "📋 Chuck Norris Joke Categories:"
|
||||
|
||||
FOR EACH category IN categories
|
||||
TALK "• " + category
|
||||
END FOR
|
||||
|
||||
RETURN categories
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Chuck Norris Joke by Category
|
||||
REM ============================================
|
||||
PARAM category AS string LIKE "dev"
|
||||
DESCRIPTION "Get a random Chuck Norris joke from a specific category"
|
||||
|
||||
joke_url = "https://api.chucknorris.io/jokes/random?category=" + category
|
||||
|
||||
chuck_joke = GET joke_url
|
||||
|
||||
TALK "😄 Chuck Norris " + category + " Joke:"
|
||||
TALK chuck_joke.value
|
||||
|
||||
RETURN chuck_joke.value
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Dad Joke
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random dad joke from icanhazdadjoke"
|
||||
|
||||
SET HEADER "Accept" = "application/json"
|
||||
|
||||
dad_joke = GET "https://icanhazdadjoke.com/"
|
||||
|
||||
TALK "👨 Dad Joke:"
|
||||
TALK dad_joke.joke
|
||||
|
||||
RETURN dad_joke.joke
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Search Dad Jokes
|
||||
REM ============================================
|
||||
PARAM search_term AS string LIKE "cat"
|
||||
DESCRIPTION "Search for dad jokes containing a specific term"
|
||||
|
||||
SET HEADER "Accept" = "application/json"
|
||||
|
||||
search_url = "https://icanhazdadjoke.com/search?term=" + search_term
|
||||
|
||||
results = GET search_url
|
||||
|
||||
TALK "🔍 Found " + results.total_jokes + " dad jokes about '" + search_term + "':"
|
||||
|
||||
counter = 0
|
||||
FOR EACH joke IN results.results
|
||||
IF counter < 5 THEN
|
||||
TALK ""
|
||||
TALK "😄 " + joke.joke
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF results.total_jokes > 5 THEN
|
||||
TALK ""
|
||||
TALK "... and " + (results.total_jokes - 5) + " more jokes!"
|
||||
END IF
|
||||
|
||||
RETURN results.results
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Bored Activity
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random activity suggestion when bored"
|
||||
|
||||
activity = GET "https://www.boredapi.com/api/activity"
|
||||
|
||||
TALK "💡 Activity Suggestion:"
|
||||
TALK activity.activity
|
||||
TALK ""
|
||||
TALK "📊 Type: " + activity.type
|
||||
TALK "👥 Participants: " + activity.participants
|
||||
TALK "💰 Price: " + activity.price
|
||||
|
||||
IF activity.link THEN
|
||||
TALK "🔗 Link: " + activity.link
|
||||
END IF
|
||||
|
||||
RETURN activity
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Bored Activity by Type
|
||||
REM ============================================
|
||||
PARAM activity_type AS "education", "recreational", "social", "diy", "charity", "cooking", "relaxation", "music", "busywork"
|
||||
DESCRIPTION "Get a random activity suggestion of a specific type"
|
||||
|
||||
activity_url = "https://www.boredapi.com/api/activity?type=" + activity_type
|
||||
|
||||
activity = GET activity_url
|
||||
|
||||
TALK "💡 " + activity_type + " Activity Suggestion:"
|
||||
TALK activity.activity
|
||||
TALK ""
|
||||
TALK "👥 Participants: " + activity.participants
|
||||
TALK "💰 Price level: " + activity.price
|
||||
|
||||
RETURN activity
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Random Useless Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random useless but true fact"
|
||||
|
||||
fact = GET "https://uselessfacts.jsph.pl/random.json?language=en"
|
||||
|
||||
TALK "🤓 Random Useless Fact:"
|
||||
TALK fact.text
|
||||
|
||||
RETURN fact.text
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Random Fun Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random fun fact"
|
||||
|
||||
fun_fact = GET "https://uselessfacts.jsph.pl/api/v2/facts/random"
|
||||
|
||||
TALK "🎉 Random Fun Fact:"
|
||||
TALK fun_fact.text
|
||||
|
||||
RETURN fun_fact.text
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Kanye West Quote
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Kanye West quote"
|
||||
|
||||
kanye = GET "https://api.kanye.rest/"
|
||||
|
||||
TALK "🎤 Kanye West says:"
|
||||
TALK '"' + kanye.quote + '"'
|
||||
|
||||
RETURN kanye.quote
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Advice Slip
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random piece of advice"
|
||||
|
||||
advice = GET "https://api.adviceslip.com/advice"
|
||||
|
||||
TALK "💭 Random Advice:"
|
||||
TALK advice.slip.advice
|
||||
|
||||
RETURN advice.slip.advice
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Search Advice
|
||||
REM ============================================
|
||||
PARAM query AS string LIKE "love"
|
||||
DESCRIPTION "Search for advice containing a specific word"
|
||||
|
||||
search_url = "https://api.adviceslip.com/advice/search/" + query
|
||||
|
||||
results = GET search_url
|
||||
|
||||
IF results.total_results > 0 THEN
|
||||
TALK "💭 Found " + results.total_results + " advice about '" + query + "':"
|
||||
|
||||
counter = 0
|
||||
FOR EACH slip IN results.slips
|
||||
IF counter < 5 THEN
|
||||
TALK ""
|
||||
TALK "• " + slip.advice
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF results.total_results > 5 THEN
|
||||
TALK ""
|
||||
TALK "... and " + (results.total_results - 5) + " more pieces of advice!"
|
||||
END IF
|
||||
|
||||
RETURN results.slips
|
||||
ELSE
|
||||
TALK "❌ No advice found for: " + query
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Corporate Buzzword
|
||||
REM ============================================
|
||||
DESCRIPTION "Get random corporate buzzwords"
|
||||
|
||||
buzzword = GET "https://corporatebs-generator.sameerkumar.website/"
|
||||
|
||||
TALK "💼 Corporate Buzzword Generator:"
|
||||
TALK buzzword.phrase
|
||||
|
||||
RETURN buzzword.phrase
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Yo Momma Joke
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Yo Momma joke"
|
||||
|
||||
joke = GET "https://api.yomomma.info/"
|
||||
|
||||
TALK "😂 Yo Momma Joke:"
|
||||
TALK joke.joke
|
||||
|
||||
RETURN joke.joke
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Random Quote
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random inspirational quote"
|
||||
|
||||
quote_data = GET "https://api.quotable.io/random"
|
||||
|
||||
quote_text = quote_data.content
|
||||
author = quote_data.author
|
||||
|
||||
TALK "✨ Inspirational Quote:"
|
||||
TALK '"' + quote_text + '"'
|
||||
TALK "— " + author
|
||||
|
||||
RETURN quote_data
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Quote by Author
|
||||
REM ============================================
|
||||
PARAM author AS string LIKE "einstein"
|
||||
DESCRIPTION "Get a random quote by a specific author"
|
||||
|
||||
quote_url = "https://api.quotable.io/random?author=" + author
|
||||
|
||||
quote_data = GET quote_url
|
||||
|
||||
IF quote_data.content THEN
|
||||
TALK "✨ Quote by " + quote_data.author + ":"
|
||||
TALK '"' + quote_data.content + '"'
|
||||
|
||||
RETURN quote_data
|
||||
ELSE
|
||||
TALK "❌ No quotes found for author: " + author
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Quote of the Day
|
||||
REM ============================================
|
||||
DESCRIPTION "Get the quote of the day"
|
||||
|
||||
qotd = GET "https://api.quotable.io/quotes/random?tags=inspirational"
|
||||
|
||||
IF UBOUND(qotd) > 0 THEN
|
||||
quote = qotd[0]
|
||||
|
||||
TALK "🌟 Quote of the Day:"
|
||||
TALK '"' + quote.content + '"'
|
||||
TALK "— " + quote.author
|
||||
|
||||
RETURN quote
|
||||
ELSE
|
||||
TALK "❌ Could not fetch quote of the day"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Programming Quote
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random programming quote"
|
||||
|
||||
quote = GET "https://programming-quotes-api.herokuapp.com/quotes/random"
|
||||
|
||||
TALK "💻 Programming Quote:"
|
||||
TALK '"' + quote.en + '"'
|
||||
TALK "— " + quote.author
|
||||
|
||||
RETURN quote
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Zen Quote
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Zen/Stoicism quote"
|
||||
|
||||
quote = GET "https://zenquotes.io/api/random"
|
||||
|
||||
IF UBOUND(quote) > 0 THEN
|
||||
zen_quote = quote[0]
|
||||
|
||||
TALK "🧘 Zen Quote:"
|
||||
TALK '"' + zen_quote.q + '"'
|
||||
TALK "— " + zen_quote.a
|
||||
|
||||
RETURN zen_quote
|
||||
ELSE
|
||||
TALK "❌ Could not fetch Zen quote"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Affirmation
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random positive affirmation"
|
||||
|
||||
affirmation = GET "https://www.affirmations.dev/"
|
||||
|
||||
TALK "💖 Daily Affirmation:"
|
||||
TALK affirmation.affirmation
|
||||
|
||||
RETURN affirmation.affirmation
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Random Trivia
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random trivia question"
|
||||
|
||||
trivia = GET "https://opentdb.com/api.php?amount=1"
|
||||
|
||||
IF trivia.results AND UBOUND(trivia.results) > 0 THEN
|
||||
question = trivia.results[0]
|
||||
|
||||
TALK "🎯 Trivia Question:"
|
||||
TALK "Category: " + question.category
|
||||
TALK "Difficulty: " + question.difficulty
|
||||
TALK ""
|
||||
TALK question.question
|
||||
TALK ""
|
||||
TALK "Correct Answer: " + question.correct_answer
|
||||
|
||||
IF question.incorrect_answers THEN
|
||||
TALK ""
|
||||
TALK "Other Options:"
|
||||
FOR EACH wrong IN question.incorrect_answers
|
||||
TALK "• " + wrong
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
RETURN question
|
||||
ELSE
|
||||
TALK "❌ Could not fetch trivia question"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Multiple Trivia Questions
|
||||
REM ============================================
|
||||
PARAM amount AS integer LIKE 5
|
||||
DESCRIPTION "Get multiple trivia questions"
|
||||
|
||||
trivia_url = "https://opentdb.com/api.php?amount=" + amount
|
||||
|
||||
trivia = GET trivia_url
|
||||
|
||||
IF trivia.results THEN
|
||||
TALK "🎯 " + amount + " Trivia Questions:"
|
||||
TALK ""
|
||||
|
||||
counter = 1
|
||||
FOR EACH question IN trivia.results
|
||||
TALK counter + ". " + question.question
|
||||
TALK " Category: " + question.category + " | Difficulty: " + question.difficulty
|
||||
TALK " Answer: " + question.correct_answer
|
||||
TALK ""
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
RETURN trivia.results
|
||||
ELSE
|
||||
TALK "❌ Could not fetch trivia questions"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Excuse Generator
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random excuse"
|
||||
|
||||
excuse = GET "https://excuser-three.vercel.app/v1/excuse"
|
||||
|
||||
IF excuse AND UBOUND(excuse) > 0 THEN
|
||||
excuse_obj = excuse[0]
|
||||
|
||||
TALK "🤷 Random Excuse:"
|
||||
TALK excuse_obj.excuse
|
||||
TALK ""
|
||||
TALK "Category: " + excuse_obj.category
|
||||
|
||||
RETURN excuse_obj
|
||||
ELSE
|
||||
TALK "❌ Could not generate excuse"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Insult Generator
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random insult (clean)"
|
||||
|
||||
insult = GET "https://evilinsult.com/generate_insult.php?lang=en&type=json"
|
||||
|
||||
TALK "😈 Random Insult:"
|
||||
TALK insult.insult
|
||||
|
||||
RETURN insult.insult
|
||||
|
||||
REM ============================================
|
||||
REM ENTERTAINMENT KEYWORD - Compliment Generator
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random compliment"
|
||||
|
||||
compliment = GET "https://complimentr.com/api"
|
||||
|
||||
TALK "💝 Random Compliment:"
|
||||
TALK compliment.compliment
|
||||
|
||||
RETURN compliment.compliment
|
||||
503
integration/public-apis.gbai/public-apis.gbdialog/food-apis.bas
Normal file
503
integration/public-apis.gbai/public-apis.gbdialog/food-apis.bas
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
REM General Bots: Food & Drink APIs - Free Food Data Integration
|
||||
REM Based on public-apis list - No authentication required
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Coffee Image
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random coffee image"
|
||||
|
||||
coffee_data = GET "https://coffee.alexflipnote.dev/random.json"
|
||||
|
||||
image_url = coffee_data.file
|
||||
|
||||
TALK "☕ Random Coffee Image:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Food Dish
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random food dish image from Foodish"
|
||||
|
||||
food_data = GET "https://foodish-api.herokuapp.com/api/"
|
||||
|
||||
image_url = food_data.image
|
||||
|
||||
TALK "🍽️ Random Food Dish:"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Food by Category
|
||||
REM ============================================
|
||||
PARAM category AS "biryani", "burger", "butter-chicken", "dessert", "dosa", "idly", "pasta", "pizza", "rice", "samosa"
|
||||
DESCRIPTION "Get a random food image from a specific category"
|
||||
|
||||
food_url = "https://foodish-api.herokuapp.com/api/images/" + category
|
||||
|
||||
food_data = GET food_url
|
||||
|
||||
image_url = food_data.image
|
||||
|
||||
TALK "🍽️ Random " + category + ":"
|
||||
TALK image_url
|
||||
|
||||
file = DOWNLOAD image_url
|
||||
SEND FILE file
|
||||
|
||||
RETURN image_url
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Meal Recipe
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random meal recipe from TheMealDB"
|
||||
|
||||
meal_data = GET "https://www.themealdb.com/api/json/v1/1/random.php"
|
||||
|
||||
IF meal_data.meals AND UBOUND(meal_data.meals) > 0 THEN
|
||||
meal = meal_data.meals[0]
|
||||
|
||||
TALK "🍳 Random Meal Recipe: " + meal.strMeal
|
||||
TALK "🌍 Category: " + meal.strCategory + " | Area: " + meal.strArea
|
||||
TALK ""
|
||||
TALK "📝 Instructions:"
|
||||
TALK meal.strInstructions
|
||||
TALK ""
|
||||
TALK "📷 Image: " + meal.strMealThumb
|
||||
|
||||
IF meal.strYoutube THEN
|
||||
TALK "🎥 Video: " + meal.strYoutube
|
||||
END IF
|
||||
|
||||
TALK ""
|
||||
TALK "🥘 Ingredients:"
|
||||
|
||||
REM Extract ingredients
|
||||
FOR i = 1 TO 20
|
||||
ingredient = meal["strIngredient" + i]
|
||||
measure = meal["strMeasure" + i]
|
||||
|
||||
IF ingredient <> "" AND ingredient <> NULL THEN
|
||||
TALK "• " + measure + " " + ingredient
|
||||
END IF
|
||||
NEXT i
|
||||
|
||||
file = DOWNLOAD meal.strMealThumb
|
||||
SEND FILE file
|
||||
|
||||
RETURN meal
|
||||
ELSE
|
||||
TALK "❌ Could not fetch meal recipe"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Search Meal by Name
|
||||
REM ============================================
|
||||
PARAM meal_name AS string LIKE "chicken"
|
||||
DESCRIPTION "Search for meals by name"
|
||||
|
||||
search_url = "https://www.themealdb.com/api/json/v1/1/search.php?s=" + meal_name
|
||||
|
||||
meal_data = GET search_url
|
||||
|
||||
IF meal_data.meals THEN
|
||||
TALK "🔍 Found meals matching '" + meal_name + "':"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH meal IN meal_data.meals
|
||||
IF counter < 5 THEN
|
||||
TALK "🍽️ " + meal.strMeal
|
||||
TALK " Category: " + meal.strCategory + " | Area: " + meal.strArea
|
||||
TALK " ID: " + meal.idMeal
|
||||
TALK ""
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 5 THEN
|
||||
TALK "... and " + (counter - 5) + " more meals"
|
||||
END IF
|
||||
|
||||
RETURN meal_data.meals
|
||||
ELSE
|
||||
TALK "❌ No meals found for: " + meal_name
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Cocktail Recipe
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random cocktail recipe from TheCocktailDB"
|
||||
|
||||
cocktail_data = GET "https://www.thecocktaildb.com/api/json/v1/1/random.php"
|
||||
|
||||
IF cocktail_data.drinks AND UBOUND(cocktail_data.drinks) > 0 THEN
|
||||
drink = cocktail_data.drinks[0]
|
||||
|
||||
TALK "🍹 Random Cocktail: " + drink.strDrink
|
||||
TALK "🏷️ Category: " + drink.strCategory
|
||||
TALK "🥃 Glass: " + drink.strGlass
|
||||
TALK ""
|
||||
TALK "📝 Instructions:"
|
||||
TALK drink.strInstructions
|
||||
TALK ""
|
||||
TALK "🍸 Ingredients:"
|
||||
|
||||
REM Extract ingredients
|
||||
FOR i = 1 TO 15
|
||||
ingredient = drink["strIngredient" + i]
|
||||
measure = drink["strMeasure" + i]
|
||||
|
||||
IF ingredient <> "" AND ingredient <> NULL THEN
|
||||
IF measure <> "" AND measure <> NULL THEN
|
||||
TALK "• " + measure + " " + ingredient
|
||||
ELSE
|
||||
TALK "• " + ingredient
|
||||
END IF
|
||||
END IF
|
||||
NEXT i
|
||||
|
||||
TALK ""
|
||||
TALK "📷 Image: " + drink.strDrinkThumb
|
||||
|
||||
file = DOWNLOAD drink.strDrinkThumb
|
||||
SEND FILE file
|
||||
|
||||
RETURN drink
|
||||
ELSE
|
||||
TALK "❌ Could not fetch cocktail recipe"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Search Cocktail by Name
|
||||
REM ============================================
|
||||
PARAM cocktail_name AS string LIKE "margarita"
|
||||
DESCRIPTION "Search for cocktails by name"
|
||||
|
||||
search_url = "https://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + cocktail_name
|
||||
|
||||
cocktail_data = GET search_url
|
||||
|
||||
IF cocktail_data.drinks THEN
|
||||
TALK "🔍 Found cocktails matching '" + cocktail_name + "':"
|
||||
TALK ""
|
||||
|
||||
FOR EACH drink IN cocktail_data.drinks
|
||||
TALK "🍹 " + drink.strDrink
|
||||
TALK " Category: " + drink.strCategory + " | Glass: " + drink.strGlass
|
||||
TALK " Alcoholic: " + drink.strAlcoholic
|
||||
TALK ""
|
||||
END FOR
|
||||
|
||||
RETURN cocktail_data.drinks
|
||||
ELSE
|
||||
TALK "❌ No cocktails found for: " + cocktail_name
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Search Cocktail by Ingredient
|
||||
REM ============================================
|
||||
PARAM ingredient AS string LIKE "vodka"
|
||||
DESCRIPTION "Search for cocktails by ingredient"
|
||||
|
||||
search_url = "https://www.thecocktaildb.com/api/json/v1/1/filter.php?i=" + ingredient
|
||||
|
||||
cocktail_data = GET search_url
|
||||
|
||||
IF cocktail_data.drinks THEN
|
||||
TALK "🔍 Found " + UBOUND(cocktail_data.drinks) + " cocktails with " + ingredient + ":"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH drink IN cocktail_data.drinks
|
||||
IF counter < 10 THEN
|
||||
TALK "🍹 " + drink.strDrink + " (ID: " + drink.idDrink + ")"
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 10 THEN
|
||||
TALK "... and " + (counter - 10) + " more cocktails"
|
||||
END IF
|
||||
|
||||
RETURN cocktail_data.drinks
|
||||
ELSE
|
||||
TALK "❌ No cocktails found with ingredient: " + ingredient
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Fruit Information
|
||||
REM ============================================
|
||||
PARAM fruit_name AS string LIKE "apple"
|
||||
DESCRIPTION "Get nutritional information about a fruit"
|
||||
|
||||
fruit_url = "https://fruityvice.com/api/fruit/" + fruit_name
|
||||
|
||||
fruit_data = GET fruit_url
|
||||
|
||||
IF fruit_data.name THEN
|
||||
TALK "🍎 Fruit Information: " + fruit_data.name
|
||||
TALK "🏷️ Family: " + fruit_data.family
|
||||
TALK "🌳 Genus: " + fruit_data.genus
|
||||
TALK "🆔 ID: " + fruit_data.id
|
||||
TALK ""
|
||||
TALK "📊 Nutritional Information (per 100g):"
|
||||
TALK "• Calories: " + fruit_data.nutritions.calories
|
||||
TALK "• Carbohydrates: " + fruit_data.nutritions.carbohydrates + "g"
|
||||
TALK "• Protein: " + fruit_data.nutritions.protein + "g"
|
||||
TALK "• Fat: " + fruit_data.nutritions.fat + "g"
|
||||
TALK "• Sugar: " + fruit_data.nutritions.sugar + "g"
|
||||
|
||||
RETURN fruit_data
|
||||
ELSE
|
||||
TALK "❌ Fruit not found: " + fruit_name
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - All Fruits List
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a list of all available fruits"
|
||||
|
||||
fruits_data = GET "https://fruityvice.com/api/fruit/all"
|
||||
|
||||
IF fruits_data THEN
|
||||
TALK "🍓 Available Fruits (" + UBOUND(fruits_data) + " total):"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH fruit IN fruits_data
|
||||
IF counter < 20 THEN
|
||||
TALK "• " + fruit.name + " (" + fruit.family + ")"
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 20 THEN
|
||||
TALK "... and " + (counter - 20) + " more fruits"
|
||||
END IF
|
||||
|
||||
RETURN fruits_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch fruits list"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Fruits by Family
|
||||
REM ============================================
|
||||
PARAM family AS string LIKE "Rosaceae"
|
||||
DESCRIPTION "Get fruits from a specific family"
|
||||
|
||||
family_url = "https://fruityvice.com/api/fruit/family/" + family
|
||||
|
||||
fruits_data = GET family_url
|
||||
|
||||
IF fruits_data THEN
|
||||
TALK "🍎 Fruits from " + family + " family:"
|
||||
TALK ""
|
||||
|
||||
FOR EACH fruit IN fruits_data
|
||||
TALK "• " + fruit.name + " (Genus: " + fruit.genus + ")"
|
||||
END FOR
|
||||
|
||||
RETURN fruits_data
|
||||
ELSE
|
||||
TALK "❌ No fruits found for family: " + family
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Random Taco Recipe
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random taco recipe from TacoFancy"
|
||||
|
||||
taco_data = GET "http://taco-randomizer.herokuapp.com/random/"
|
||||
|
||||
IF taco_data THEN
|
||||
TALK "🌮 Random Taco Recipe:"
|
||||
TALK ""
|
||||
|
||||
IF taco_data.base_layer THEN
|
||||
TALK "🫓 Base Layer: " + taco_data.base_layer.name
|
||||
TALK taco_data.base_layer.recipe
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
IF taco_data.mixin THEN
|
||||
TALK "🥗 Mixin: " + taco_data.mixin.name
|
||||
TALK taco_data.mixin.recipe
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
IF taco_data.condiment THEN
|
||||
TALK "🧂 Condiment: " + taco_data.condiment.name
|
||||
TALK taco_data.condiment.recipe
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
IF taco_data.seasoning THEN
|
||||
TALK "🌶️ Seasoning: " + taco_data.seasoning.name
|
||||
TALK taco_data.seasoning.recipe
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
IF taco_data.shell THEN
|
||||
TALK "🌮 Shell: " + taco_data.shell.name
|
||||
TALK taco_data.shell.recipe
|
||||
END IF
|
||||
|
||||
RETURN taco_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch taco recipe"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - PunkAPI Beer Info
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random beer recipe from PunkAPI"
|
||||
|
||||
beer_data = GET "https://api.punkapi.com/v2/beers/random"
|
||||
|
||||
IF beer_data AND UBOUND(beer_data) > 0 THEN
|
||||
beer = beer_data[0]
|
||||
|
||||
TALK "🍺 Beer Information: " + beer.name
|
||||
TALK "📝 Tagline: " + beer.tagline
|
||||
TALK ""
|
||||
TALK "📊 Details:"
|
||||
TALK "• ABV: " + beer.abv + "%"
|
||||
TALK "• IBU: " + beer.ibu
|
||||
TALK "• EBC: " + beer.ebc
|
||||
TALK "• First Brewed: " + beer.first_brewed
|
||||
TALK ""
|
||||
TALK "📖 Description:"
|
||||
TALK beer.description
|
||||
TALK ""
|
||||
|
||||
IF beer.food_pairing THEN
|
||||
TALK "🍽️ Food Pairing:"
|
||||
FOR EACH pairing IN beer.food_pairing
|
||||
TALK "• " + pairing
|
||||
END FOR
|
||||
TALK ""
|
||||
END IF
|
||||
|
||||
IF beer.brewers_tips THEN
|
||||
TALK "💡 Brewer's Tips:"
|
||||
TALK beer.brewers_tips
|
||||
END IF
|
||||
|
||||
IF beer.image_url THEN
|
||||
TALK ""
|
||||
TALK "📷 Image: " + beer.image_url
|
||||
file = DOWNLOAD beer.image_url
|
||||
SEND FILE file
|
||||
END IF
|
||||
|
||||
RETURN beer
|
||||
ELSE
|
||||
TALK "❌ Could not fetch beer information"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Search Beer by Name
|
||||
REM ============================================
|
||||
PARAM beer_name AS string LIKE "punk"
|
||||
DESCRIPTION "Search for beers by name"
|
||||
|
||||
search_url = "https://api.punkapi.com/v2/beers?beer_name=" + beer_name
|
||||
|
||||
beer_data = GET search_url
|
||||
|
||||
IF beer_data AND UBOUND(beer_data) > 0 THEN
|
||||
TALK "🔍 Found " + UBOUND(beer_data) + " beer(s) matching '" + beer_name + "':"
|
||||
TALK ""
|
||||
|
||||
FOR EACH beer IN beer_data
|
||||
TALK "🍺 " + beer.name
|
||||
TALK " " + beer.tagline
|
||||
TALK " ABV: " + beer.abv + "% | IBU: " + beer.ibu
|
||||
TALK " First Brewed: " + beer.first_brewed
|
||||
TALK ""
|
||||
END FOR
|
||||
|
||||
RETURN beer_data
|
||||
ELSE
|
||||
TALK "❌ No beers found for: " + beer_name
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - High ABV Beers
|
||||
REM ============================================
|
||||
PARAM min_abv AS number LIKE 8.0
|
||||
DESCRIPTION "Get beers with ABV higher than specified"
|
||||
|
||||
abv_url = "https://api.punkapi.com/v2/beers?abv_gt=" + min_abv
|
||||
|
||||
beer_data = GET abv_url
|
||||
|
||||
IF beer_data THEN
|
||||
TALK "🍺 Beers with ABV > " + min_abv + "%:"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH beer IN beer_data
|
||||
IF counter < 10 THEN
|
||||
TALK "🍺 " + beer.name + " - " + beer.abv + "% ABV"
|
||||
TALK " " + beer.tagline
|
||||
TALK ""
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 10 THEN
|
||||
TALK "... and " + (counter - 10) + " more beers"
|
||||
END IF
|
||||
|
||||
RETURN beer_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch high ABV beers"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM FOOD KEYWORD - Bacon Ipsum Text
|
||||
REM ============================================
|
||||
PARAM paragraphs AS integer LIKE 3
|
||||
DESCRIPTION "Generate bacon-themed lorem ipsum text"
|
||||
|
||||
bacon_url = "https://baconipsum.com/api/?type=meat-and-filler¶s=" + paragraphs
|
||||
|
||||
bacon_text = GET bacon_url
|
||||
|
||||
IF bacon_text THEN
|
||||
TALK "🥓 Bacon Ipsum Text:"
|
||||
TALK ""
|
||||
|
||||
FOR EACH paragraph IN bacon_text
|
||||
TALK paragraph
|
||||
TALK ""
|
||||
END FOR
|
||||
|
||||
RETURN bacon_text
|
||||
ELSE
|
||||
TALK "❌ Could not generate bacon ipsum"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
|
@ -0,0 +1,595 @@
|
|||
REM General Bots: Science, Space & Books APIs - Free Knowledge Integration
|
||||
REM Based on public-apis list - No authentication required
|
||||
|
||||
REM ============================================
|
||||
REM SPACE KEYWORD - Random Space Image (NASA APOD)
|
||||
REM ============================================
|
||||
DESCRIPTION "Get NASA's Astronomy Picture of the Day (demo key)"
|
||||
|
||||
apod_data = GET "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
|
||||
|
||||
IF apod_data.title THEN
|
||||
TALK "🌌 NASA Astronomy Picture of the Day:"
|
||||
TALK "📸 " + apod_data.title
|
||||
TALK ""
|
||||
TALK "📅 Date: " + apod_data.date
|
||||
TALK "📝 Explanation:"
|
||||
TALK apod_data.explanation
|
||||
TALK ""
|
||||
|
||||
IF apod_data.media_type = "image" THEN
|
||||
TALK "🖼️ Image URL: " + apod_data.url
|
||||
file = DOWNLOAD apod_data.url
|
||||
SEND FILE file
|
||||
ELSE IF apod_data.media_type = "video" THEN
|
||||
TALK "🎥 Video URL: " + apod_data.url
|
||||
END IF
|
||||
|
||||
IF apod_data.copyright THEN
|
||||
TALK "©️ Copyright: " + apod_data.copyright
|
||||
END IF
|
||||
|
||||
RETURN apod_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch NASA APOD"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SPACE KEYWORD - ISS Current Location
|
||||
REM ============================================
|
||||
DESCRIPTION "Get current location of International Space Station"
|
||||
|
||||
iss_location = GET "http://api.open-notify.org/iss-now.json"
|
||||
|
||||
IF iss_location.message = "success" THEN
|
||||
lat = iss_location.iss_position.latitude
|
||||
lon = iss_location.iss_position.longitude
|
||||
timestamp = iss_location.timestamp
|
||||
|
||||
TALK "🛰️ International Space Station Location:"
|
||||
TALK "📍 Latitude: " + lat
|
||||
TALK "📍 Longitude: " + lon
|
||||
TALK "⏰ Timestamp: " + timestamp
|
||||
TALK ""
|
||||
TALK "🗺️ View on map: https://www.google.com/maps?q=" + lat + "," + lon
|
||||
|
||||
RETURN iss_location
|
||||
ELSE
|
||||
TALK "❌ Could not fetch ISS location"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SPACE KEYWORD - People in Space Right Now
|
||||
REM ============================================
|
||||
DESCRIPTION "Get list of astronauts currently in space"
|
||||
|
||||
space_people = GET "http://api.open-notify.org/astros.json"
|
||||
|
||||
IF space_people.message = "success" THEN
|
||||
TALK "👨🚀 People Currently in Space: " + space_people.number
|
||||
TALK ""
|
||||
|
||||
FOR EACH person IN space_people.people
|
||||
TALK "• " + person.name + " (" + person.craft + ")"
|
||||
END FOR
|
||||
|
||||
RETURN space_people
|
||||
ELSE
|
||||
TALK "❌ Could not fetch space crew data"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SPACE KEYWORD - SpaceX Launch Info
|
||||
REM ============================================
|
||||
DESCRIPTION "Get latest SpaceX launch information"
|
||||
|
||||
launch_data = GET "https://api.spacexdata.com/v4/launches/latest"
|
||||
|
||||
IF launch_data.name THEN
|
||||
TALK "🚀 Latest SpaceX Launch:"
|
||||
TALK "Mission: " + launch_data.name
|
||||
TALK ""
|
||||
TALK "📅 Date: " + launch_data.date_utc
|
||||
TALK "🎯 Success: " + launch_data.success
|
||||
TALK "🔢 Flight Number: " + launch_data.flight_number
|
||||
|
||||
IF launch_data.details THEN
|
||||
TALK ""
|
||||
TALK "📝 Details:"
|
||||
TALK launch_data.details
|
||||
END IF
|
||||
|
||||
IF launch_data.links.webcast THEN
|
||||
TALK ""
|
||||
TALK "🎥 Webcast: " + launch_data.links.webcast
|
||||
END IF
|
||||
|
||||
IF launch_data.links.patch.small THEN
|
||||
TALK ""
|
||||
TALK "🏴 Mission Patch:"
|
||||
file = DOWNLOAD launch_data.links.patch.small
|
||||
SEND FILE file
|
||||
END IF
|
||||
|
||||
RETURN launch_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch SpaceX launch data"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SPACE KEYWORD - Next SpaceX Launch
|
||||
REM ============================================
|
||||
DESCRIPTION "Get next upcoming SpaceX launch"
|
||||
|
||||
next_launch = GET "https://api.spacexdata.com/v4/launches/next"
|
||||
|
||||
IF next_launch.name THEN
|
||||
TALK "🚀 Next SpaceX Launch:"
|
||||
TALK "Mission: " + next_launch.name
|
||||
TALK ""
|
||||
TALK "📅 Scheduled: " + next_launch.date_utc
|
||||
TALK "🔢 Flight Number: " + next_launch.flight_number
|
||||
|
||||
IF next_launch.details THEN
|
||||
TALK ""
|
||||
TALK "📝 Details:"
|
||||
TALK next_launch.details
|
||||
END IF
|
||||
|
||||
RETURN next_launch
|
||||
ELSE
|
||||
TALK "❌ Could not fetch next SpaceX launch"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Random Math Problem
|
||||
REM ============================================
|
||||
PARAM difficulty AS "easy", "medium", "hard"
|
||||
DESCRIPTION "Get a random math problem to solve"
|
||||
|
||||
REM Generate based on difficulty
|
||||
result = NEW OBJECT
|
||||
|
||||
IF difficulty = "easy" THEN
|
||||
num1 = INT(RND() * 10) + 1
|
||||
num2 = INT(RND() * 10) + 1
|
||||
operation = "+"
|
||||
answer = num1 + num2
|
||||
|
||||
ELSE IF difficulty = "medium" THEN
|
||||
num1 = INT(RND() * 50) + 10
|
||||
num2 = INT(RND() * 50) + 10
|
||||
operations = NEW ARRAY
|
||||
operations.PUSH("+")
|
||||
operations.PUSH("-")
|
||||
operations.PUSH("*")
|
||||
operation = operations[INT(RND() * 3)]
|
||||
|
||||
IF operation = "+" THEN
|
||||
answer = num1 + num2
|
||||
ELSE IF operation = "-" THEN
|
||||
answer = num1 - num2
|
||||
ELSE
|
||||
answer = num1 * num2
|
||||
END IF
|
||||
|
||||
ELSE IF difficulty = "hard" THEN
|
||||
num1 = INT(RND() * 100) + 50
|
||||
num2 = INT(RND() * 20) + 5
|
||||
operations = NEW ARRAY
|
||||
operations.PUSH("+")
|
||||
operations.PUSH("-")
|
||||
operations.PUSH("*")
|
||||
operations.PUSH("/")
|
||||
operation = operations[INT(RND() * 4)]
|
||||
|
||||
IF operation = "+" THEN
|
||||
answer = num1 + num2
|
||||
ELSE IF operation = "-" THEN
|
||||
answer = num1 - num2
|
||||
ELSE IF operation = "*" THEN
|
||||
answer = num1 * num2
|
||||
ELSE
|
||||
answer = num1 / num2
|
||||
END IF
|
||||
END IF
|
||||
|
||||
result.problem = num1 + " " + operation + " " + num2
|
||||
result.answer = answer
|
||||
result.difficulty = difficulty
|
||||
|
||||
TALK "🧮 Math Problem (" + difficulty + "):"
|
||||
TALK result.problem + " = ?"
|
||||
TALK ""
|
||||
TALK "💡 Think about it..."
|
||||
WAIT 3
|
||||
TALK ""
|
||||
TALK "✅ Answer: " + answer
|
||||
|
||||
RETURN result
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Periodic Table Element
|
||||
REM ============================================
|
||||
PARAM element AS string LIKE "hydrogen"
|
||||
DESCRIPTION "Get information about a chemical element"
|
||||
|
||||
element_url = "https://neelpatel05.pythonanywhere.com/element/atomicname?atomicname=" + element
|
||||
|
||||
element_data = GET element_url
|
||||
|
||||
IF element_data.atomicName THEN
|
||||
TALK "🧪 Chemical Element: " + element_data.atomicName
|
||||
TALK ""
|
||||
TALK "⚛️ Symbol: " + element_data.symbol
|
||||
TALK "🔢 Atomic Number: " + element_data.atomicNumber
|
||||
TALK "⚖️ Atomic Mass: " + element_data.atomicMass
|
||||
TALK "📊 Group: " + element_data.groupBlock
|
||||
TALK "🌡️ Boiling Point: " + element_data.boilingPoint
|
||||
TALK "🌡️ Melting Point: " + element_data.meltingPoint
|
||||
TALK "📏 Density: " + element_data.density
|
||||
TALK "⚡ Electronegativity: " + element_data.electronegativity
|
||||
TALK "📅 Year Discovered: " + element_data.yearDiscovered
|
||||
|
||||
RETURN element_data
|
||||
ELSE
|
||||
TALK "❌ Element not found: " + element
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Random Science Fact
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random science fact"
|
||||
|
||||
science_facts = NEW ARRAY
|
||||
science_facts.PUSH("The human body contains about 37.2 trillion cells.")
|
||||
science_facts.PUSH("Light travels at approximately 299,792 kilometers per second.")
|
||||
science_facts.PUSH("Water covers about 71% of Earth's surface.")
|
||||
science_facts.PUSH("The human brain contains about 86 billion neurons.")
|
||||
science_facts.PUSH("DNA stands for Deoxyribonucleic Acid.")
|
||||
science_facts.PUSH("Sound travels faster through water than air.")
|
||||
science_facts.PUSH("Octopuses have three hearts and blue blood.")
|
||||
science_facts.PUSH("The sun is about 109 times wider than Earth.")
|
||||
science_facts.PUSH("A single bolt of lightning contains 1 billion volts.")
|
||||
science_facts.PUSH("Humans share about 60% of their DNA with bananas.")
|
||||
|
||||
random_index = INT(RND() * UBOUND(science_facts))
|
||||
fact = science_facts[random_index]
|
||||
|
||||
TALK "🔬 Random Science Fact:"
|
||||
TALK fact
|
||||
|
||||
RETURN fact
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Earthquake Data
|
||||
REM ============================================
|
||||
DESCRIPTION "Get recent earthquake data worldwide"
|
||||
|
||||
quake_url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_month.geojson"
|
||||
|
||||
quake_data = GET quake_url
|
||||
|
||||
IF quake_data.features THEN
|
||||
TALK "🌍 Significant Earthquakes (Last Month):"
|
||||
TALK "Total Events: " + quake_data.metadata.count
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH quake IN quake_data.features
|
||||
IF counter < 5 THEN
|
||||
props = quake.properties
|
||||
magnitude = props.mag
|
||||
place = props.place
|
||||
time_ms = props.time
|
||||
|
||||
TALK "📍 " + place
|
||||
TALK " Magnitude: " + magnitude
|
||||
TALK " Time: " + time_ms
|
||||
TALK ""
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 5 THEN
|
||||
TALK "... and " + (counter - 5) + " more earthquakes"
|
||||
END IF
|
||||
|
||||
RETURN quake_data.features
|
||||
ELSE
|
||||
TALK "❌ Could not fetch earthquake data"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Random Quote from Book
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random book quote"
|
||||
|
||||
quote = GET "https://api.quotable.io/random?tags=literature"
|
||||
|
||||
IF quote.content THEN
|
||||
TALK "📚 Book Quote:"
|
||||
TALK '"' + quote.content + '"'
|
||||
TALK ""
|
||||
TALK "— " + quote.author
|
||||
|
||||
IF quote.tags THEN
|
||||
TALK ""
|
||||
TALK "Tags: " + JOIN(quote.tags, ", ")
|
||||
END IF
|
||||
|
||||
RETURN quote
|
||||
ELSE
|
||||
TALK "❌ Could not fetch book quote"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Bible Verse of the Day
|
||||
REM ============================================
|
||||
DESCRIPTION "Get Bible verse of the day"
|
||||
|
||||
verse = GET "https://beta.ourmanna.com/api/v1/get?format=json"
|
||||
|
||||
IF verse.verse.details.text THEN
|
||||
TALK "📖 Bible Verse of the Day:"
|
||||
TALK verse.verse.details.reference
|
||||
TALK ""
|
||||
TALK verse.verse.details.text
|
||||
TALK ""
|
||||
TALK "Version: " + verse.verse.details.version
|
||||
|
||||
RETURN verse
|
||||
ELSE
|
||||
TALK "❌ Could not fetch Bible verse"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Random Quran Verse
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random verse from the Quran"
|
||||
|
||||
REM Random surah (1-114) and ayah
|
||||
surah = INT(RND() * 114) + 1
|
||||
ayah = INT(RND() * 20) + 1
|
||||
|
||||
quran_url = "https://api.alquran.cloud/v1/ayah/" + surah + ":" + ayah + "/en.asad"
|
||||
|
||||
quran_data = GET quran_url
|
||||
|
||||
IF quran_data.data.text THEN
|
||||
TALK "📖 Quran Verse:"
|
||||
TALK "Surah " + quran_data.data.surah.number + ": " + quran_data.data.surah.englishName
|
||||
TALK "Ayah " + quran_data.data.numberInSurah
|
||||
TALK ""
|
||||
TALK quran_data.data.text
|
||||
|
||||
RETURN quran_data.data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch Quran verse"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Poetry Search
|
||||
REM ============================================
|
||||
PARAM search_term AS string LIKE "love"
|
||||
DESCRIPTION "Search for poems containing a specific word"
|
||||
|
||||
poetry_url = "https://poetrydb.org/lines/" + search_term
|
||||
|
||||
poems = GET poetry_url
|
||||
|
||||
IF poems AND UBOUND(poems) > 0 THEN
|
||||
TALK "📜 Found " + UBOUND(poems) + " poems with '" + search_term + "':"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH poem IN poems
|
||||
IF counter < 3 THEN
|
||||
TALK "📖 " + poem.title
|
||||
TALK "✍️ By " + poem.author
|
||||
TALK ""
|
||||
|
||||
REM Show first few lines
|
||||
line_count = 0
|
||||
FOR EACH line IN poem.lines
|
||||
IF line_count < 4 THEN
|
||||
TALK " " + line
|
||||
END IF
|
||||
line_count = line_count + 1
|
||||
END FOR
|
||||
TALK ""
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 3 THEN
|
||||
TALK "... and " + (counter - 3) + " more poems"
|
||||
END IF
|
||||
|
||||
RETURN poems
|
||||
ELSE
|
||||
TALK "❌ No poems found for: " + search_term
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Random Poem
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random poem"
|
||||
|
||||
REM Get random poem
|
||||
poem_data = GET "https://poetrydb.org/random/1"
|
||||
|
||||
IF poem_data AND UBOUND(poem_data) > 0 THEN
|
||||
poem = poem_data[0]
|
||||
|
||||
TALK "📜 Random Poem:"
|
||||
TALK ""
|
||||
TALK "📖 " + poem.title
|
||||
TALK "✍️ By " + poem.author
|
||||
TALK ""
|
||||
|
||||
FOR EACH line IN poem.lines
|
||||
TALK line
|
||||
END FOR
|
||||
|
||||
RETURN poem
|
||||
ELSE
|
||||
TALK "❌ Could not fetch random poem"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM BOOKS KEYWORD - Shakespeare Quote
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Shakespeare quote"
|
||||
|
||||
shakespeare = GET "https://api.quotable.io/random?tags=famous-quotes&author=william-shakespeare"
|
||||
|
||||
IF shakespeare.content THEN
|
||||
TALK "🎭 Shakespeare Quote:"
|
||||
TALK '"' + shakespeare.content + '"'
|
||||
TALK ""
|
||||
TALK "— William Shakespeare"
|
||||
|
||||
RETURN shakespeare
|
||||
ELSE
|
||||
REM Fallback to any Shakespeare source
|
||||
TALK "🎭 Shakespeare Quote:"
|
||||
TALK '"To be, or not to be, that is the question."'
|
||||
TALK ""
|
||||
TALK "— William Shakespeare (Hamlet)"
|
||||
|
||||
result = NEW OBJECT
|
||||
result.content = "To be, or not to be, that is the question."
|
||||
result.author = "William Shakespeare"
|
||||
RETURN result
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Random Wikipedia Summary
|
||||
REM ============================================
|
||||
DESCRIPTION "Get a random Wikipedia article summary"
|
||||
|
||||
wiki_data = GET "https://en.wikipedia.org/api/rest_v1/page/random/summary"
|
||||
|
||||
IF wiki_data.title THEN
|
||||
TALK "📚 Random Wikipedia Article:"
|
||||
TALK "Title: " + wiki_data.title
|
||||
TALK ""
|
||||
TALK "📝 Summary:"
|
||||
TALK wiki_data.extract
|
||||
TALK ""
|
||||
TALK "🔗 Read more: " + wiki_data.content_urls.desktop.page
|
||||
|
||||
IF wiki_data.thumbnail THEN
|
||||
TALK ""
|
||||
TALK "🖼️ Thumbnail:"
|
||||
file = DOWNLOAD wiki_data.thumbnail.source
|
||||
SEND FILE file
|
||||
END IF
|
||||
|
||||
RETURN wiki_data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch Wikipedia article"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Today in History
|
||||
REM ============================================
|
||||
DESCRIPTION "Get historical events that happened today"
|
||||
|
||||
today = NOW()
|
||||
month = MONTH(today)
|
||||
day = DAY(today)
|
||||
|
||||
history_url = "https://history.muffinlabs.com/date/" + month + "/" + day
|
||||
|
||||
history_data = GET history_url
|
||||
|
||||
IF history_data.data.Events THEN
|
||||
TALK "📅 Today in History (" + month + "/" + day + "):"
|
||||
TALK ""
|
||||
|
||||
counter = 0
|
||||
FOR EACH event IN history_data.data.Events
|
||||
IF counter < 5 THEN
|
||||
TALK "📜 " + event.year + ": " + event.text
|
||||
TALK ""
|
||||
END IF
|
||||
counter = counter + 1
|
||||
END FOR
|
||||
|
||||
IF counter > 5 THEN
|
||||
TALK "... and " + (counter - 5) + " more events"
|
||||
END IF
|
||||
|
||||
IF history_data.data.Births THEN
|
||||
TALK ""
|
||||
TALK "🎂 Notable Births:"
|
||||
birth_count = 0
|
||||
FOR EACH birth IN history_data.data.Births
|
||||
IF birth_count < 3 THEN
|
||||
TALK "• " + birth.year + ": " + birth.text
|
||||
END IF
|
||||
birth_count = birth_count + 1
|
||||
END FOR
|
||||
END IF
|
||||
|
||||
RETURN history_data.data
|
||||
ELSE
|
||||
TALK "❌ Could not fetch historical data"
|
||||
RETURN NULL
|
||||
END IF
|
||||
|
||||
REM ============================================
|
||||
REM SCIENCE KEYWORD - Age Calculator
|
||||
REM ============================================
|
||||
PARAM birth_date AS date LIKE "1990-01-15"
|
||||
DESCRIPTION "Calculate age and interesting facts"
|
||||
|
||||
birth = DATEVALUE(birth_date)
|
||||
today = NOW()
|
||||
|
||||
years = YEAR(today) - YEAR(birth)
|
||||
days = DATEDIFF(today, birth, "d")
|
||||
hours = days * 24
|
||||
minutes = hours * 60
|
||||
|
||||
TALK "🎂 Age Calculator:"
|
||||
TALK "Birth Date: " + birth_date
|
||||
TALK ""
|
||||
TALK "📊 You are:"
|
||||
TALK "• " + years + " years old"
|
||||
TALK "• " + days + " days old"
|
||||
TALK "• " + hours + " hours old"
|
||||
TALK "• " + minutes + " minutes old"
|
||||
TALK ""
|
||||
|
||||
REM Calculate next birthday
|
||||
next_birthday = DATEVALUE(YEAR(today) + "-" + MONTH(birth) + "-" + DAY(birth))
|
||||
IF next_birthday < today THEN
|
||||
next_birthday = DATEADD(next_birthday, 1, "yyyy")
|
||||
END IF
|
||||
days_until = DATEDIFF(next_birthday, today, "d")
|
||||
|
||||
TALK "🎉 Next birthday in " + days_until + " days!"
|
||||
|
||||
result = NEW OBJECT
|
||||
result.years = years
|
||||
result.days = days
|
||||
result.hours = hours
|
||||
result.next_birthday = next_birthday
|
||||
|
||||
RETURN result
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue