Commit inicial: Sales Bot - Sistema de Automatización de Ventas

- Stack completo con Mattermost, NocoDB y Sales Bot
- Procesamiento OCR de tickets con Tesseract
- Sistema de comisiones por tubos de tinte
- Comandos slash /metas y /ranking
- Documentación completa del proyecto

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 02:41:53 +00:00
commit 5d9cbd4812
21 changed files with 4625 additions and 0 deletions

329
docs/API.md Normal file
View File

@@ -0,0 +1,329 @@
# Documentación de API
## Sales Bot API
### Base URL
```
http://192.168.10.204:5000
```
---
## Endpoints
### Health Check
Verifica el estado del servicio.
```http
GET /health
```
**Respuesta exitosa (200):**
```json
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:45.123456",
"version": "1.0.0",
"services": {
"mattermost": "connected",
"nocodb": "connected"
}
}
```
---
### Webhook Mattermost
Recibe webhooks salientes de Mattermost cuando se envía un mensaje de venta.
```http
POST /webhook/mattermost
Content-Type: application/x-www-form-urlencoded
```
**Parámetros:**
| Campo | Tipo | Descripción |
|-------|------|-------------|
| token | string | Token de verificación del webhook |
| team_id | string | ID del team |
| team_domain | string | Dominio del team |
| channel_id | string | ID del canal |
| channel_name | string | Nombre del canal |
| timestamp | number | Timestamp del mensaje |
| user_id | string | ID del usuario |
| user_name | string | Nombre de usuario |
| post_id | string | ID del post |
| text | string | Texto del mensaje |
| file_ids | string | IDs de archivos adjuntos (separados por coma) |
**Respuesta exitosa (200):**
```json
{
"response_type": "comment",
"text": "✅ Venta registrada correctamente\n\n**Monto:** $1,500.00\n**Cliente:** Juan Pérez\n**Vendedor:** @vendedor1\n\n📊 **Estadísticas del día:**\n- Tubos vendidos: 5\n- Meta diaria: 3\n- Comisión: $20.00"
}
```
**Error (400):**
```json
{
"error": "Token inválido"
}
```
---
### Webhook NocoDB
Recibe webhooks de NocoDB cuando hay cambios en las tablas.
```http
POST /webhook/nocodb
Content-Type: application/json
```
**Payload:**
```json
{
"type": "records.after.insert",
"data": {
"table_name": "Ventas",
"rows": [...]
}
}
```
---
### Comando /metas
Muestra el progreso de metas del vendedor.
```http
POST /comando/metas
Content-Type: application/x-www-form-urlencoded
```
**Parámetros:**
| Campo | Tipo | Descripción |
|-------|------|-------------|
| user_name | string | Nombre de usuario que ejecuta el comando |
| channel_id | string | ID del canal |
**Respuesta:**
```json
{
"response_type": "ephemeral",
"text": "📊 **Tus metas - Enero 2024**\n\n**Tubos vendidos hoy:** 5/3 ✅\n**Comisión del día:** $20.00\n\n**Mes actual:**\n- Total tubos: 45\n- Comisión acumulada: $150.00\n- Total vendido: $15,000.00"
}
```
---
### Comando /ranking
Muestra el ranking de vendedores.
```http
POST /comando/ranking
Content-Type: application/x-www-form-urlencoded
```
**Parámetros:**
| Campo | Tipo | Descripción |
|-------|------|-------------|
| channel_id | string | ID del canal |
**Respuesta:**
```json
{
"response_type": "in_channel",
"text": "🏆 **Ranking de Vendedores - Enero 2024**\n\n1. 🥇 @vendedor1 - $25,000.00 (75 tubos)\n2. 🥈 @vendedor2 - $20,000.00 (60 tubos)\n3. 🥉 @vendedor3 - $15,000.00 (45 tubos)"
}
```
---
## NocoDB API
### Base URL
```
http://192.168.10.204:8080/api/v2
```
### Autenticación
```http
xc-token: <API_TOKEN>
```
---
### Listar Vendedores
```http
GET /tables/{TABLE_ID}/records
xc-token: <TOKEN>
```
**Respuesta:**
```json
{
"list": [
{
"Id": 1,
"username": "vendedor1",
"nombre_completo": "Juan Pérez",
"email": "juan@ejemplo.com",
"meta_diaria_tubos": 3,
"activo": true,
"fecha_registro": "2024-01-01T00:00:00.000Z"
}
],
"pageInfo": {
"totalRows": 1,
"page": 1,
"pageSize": 25
}
}
```
---
### Crear Venta
```http
POST /tables/{TABLE_ID}/records
xc-token: <TOKEN>
Content-Type: application/json
```
**Body:**
```json
{
"vendedor_username": "vendedor1",
"monto": 1500.00,
"cliente": "Juan Pérez",
"fecha_venta": "2024-01-15T10:30:00.000Z",
"estado": "completada",
"canal": "ventas-general",
"mensaje_id": "abc123",
"descripcion": "Venta de tintes"
}
```
**Respuesta:**
```json
{
"Id": 1,
"vendedor_username": "vendedor1",
"monto": 1500.00,
"cliente": "Juan Pérez",
"fecha_venta": "2024-01-15T10:30:00.000Z",
"estado": "completada",
"canal": "ventas-general",
"mensaje_id": "abc123",
"descripcion": "Venta de tintes"
}
```
---
### Filtrar Ventas por Fecha
```http
GET /tables/{TABLE_ID}/records?where=(fecha_venta,gte,2024-01-15)~and(fecha_venta,lt,2024-01-16)
xc-token: <TOKEN>
```
---
### Obtener Ranking
```http
GET /tables/{TABLE_ID}/records?sort=-monto&limit=10
xc-token: <TOKEN>
```
---
## Mattermost API
### Base URL
```
http://192.168.10.204:8065/api/v4
```
### Autenticación
```http
Authorization: Bearer <BOT_TOKEN>
```
---
### Enviar Mensaje
```http
POST /posts
Authorization: Bearer <TOKEN>
Content-Type: application/json
```
**Body:**
```json
{
"channel_id": "abc123",
"message": "Mensaje de texto"
}
```
---
### Agregar Reacción
```http
POST /reactions
Authorization: Bearer <TOKEN>
Content-Type: application/json
```
**Body:**
```json
{
"user_id": "user123",
"post_id": "post123",
"emoji_name": "white_check_mark"
}
```
---
### Obtener Archivo
```http
GET /files/{file_id}
Authorization: Bearer <TOKEN>
```
---
## Códigos de Error
| Código | Descripción |
|--------|-------------|
| 200 | Éxito |
| 400 | Solicitud inválida |
| 401 | No autorizado |
| 403 | Prohibido |
| 404 | No encontrado |
| 500 | Error interno del servidor |
## Rate Limiting
- Sales Bot: Sin límite interno
- Mattermost: 10 requests/segundo por usuario
- NocoDB: 100 requests/minuto por token

208
docs/ARQUITECTURA.md Normal file
View File

@@ -0,0 +1,208 @@
# Arquitectura del Sistema
## Diagrama General
```
┌─────────────────────────────────────┐
│ INFRAESTRUCTURA │
│ (Docker Compose) │
└─────────────────────────────────────┘
┌──────────────────────────────────────────┼──────────────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ MATTERMOST │ │ SALES BOT │ │ NOCODB │
│ (Puerto 8065) │◄────────────────►│ (Puerto 5000) │◄────────────────►│ (Puerto 8080) │
├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤
│ - Chat empresarial │ │ - Flask/Gunicorn │ │ - UI visual │
│ - Webhooks │ │ - OCR Tesseract │ │ - API REST │
│ - Comandos slash │ │ - WebSocket client │ │ - Relaciones │
│ - Archivos/imágenes │ │ - Handlers │ │ - Webhooks │
├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤
│ PostgreSQL │ │ Python 3.12 │ │ PostgreSQL │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
```
## Flujo de Datos
```
┌──────────┐ ┌────────────┐ ┌───────────┐ ┌─────────┐ ┌────────────┐
│ VENDEDOR │────►│ MATTERMOST │────►│ SALES BOT │────►│ NOCODB │────►│ RESPUESTA │
└──────────┘ └────────────┘ └───────────┘ └─────────┘ └────────────┘
│ │ │ │ │
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Mensaje Webhook POST Procesamiento Registro DB Mensaje +
+ Imagen + Payload OCR + Datos Reacción
```
## Redes Docker
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ HOST (192.168.10.204) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ mattermost-network │ │ sales-bot-network │ │ nocodb-network │ │
│ │ (bridge) │ │ (bridge) │ │ (bridge) │ │
│ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤ │
│ │ mattermost:8065 ◄┼──┼► sales-bot:5000 ◄┼──┼► nocodb:8080 │ │
│ │ postgres:5432 │ │ │ │ postgres:5432 │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Componentes del Sales Bot
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ SALES BOT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ app.py │ │ handlers.py │ │ ocr_processor │ │
│ │ ─────────────── │ │ ─────────────── │ │ ─────────────── │ │
│ │ Flask App │────►│ handle_venta │────►│ Tesseract OCR │ │
│ │ Endpoints │ │ Lógica negocio │ │ OpenCV │ │
│ │ Inicialización │ │ Comisiones │ │ Preprocesamiento│ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ websocket_ │ │ mattermost_ │ │ nocodb_ │ │
│ │ listener.py │ │ client.py │ │ client.py │ │
│ │ ─────────────── │ │ ─────────────── │ │ ─────────────── │ │
│ │ Eventos tiempo │ │ API Mattermost │ │ API NocoDB │ │
│ │ real │ │ Mensajes │ │ CRUD tablas │ │
│ │ Thread separado │ │ Archivos │ │ Comisiones │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ utils.py │ │
│ │ ─────────────────────────────────────────────────────────────── │ │
│ │ extraer_monto() │ extraer_cliente() │ extraer_tubos() │ etc. │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Proceso de Venta
```
┌────────────────────────────────────────────────────────────────────────────────┐
│ PROCESO DE REGISTRO DE VENTA │
├────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. RECEPCIÓN │
│ ┌─────────────────┐ │
│ │ Vendedor envía │ │
│ │ mensaje + foto │ │
│ └────────┬────────┘ │
│ │ │
│ 2. WEBHOOK │
│ ┌────────▼────────┐ │
│ │ Mattermost │ │
│ │ Outgoing Hook │ │
│ └────────┬────────┘ │
│ │ │
│ 3. EXTRACCIÓN │
│ ┌────────▼────────┐ ┌─────────────────┐ │
│ │ Parseo de texto │────►│ @monto 1500 │ │
│ │ │ │ @cliente Juan │ │
│ │ │ │ @tubos 5 │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ 4. OCR (si hay imagen) │
│ ┌────────▼────────┐ ┌─────────────────┐ │
│ │ Descargar img │────►│ Preprocesamiento│ │
│ │ de Mattermost │ │ OCR Tesseract │ │
│ └────────┬────────┘ │ Detección tubos │ │
│ │ └─────────────────┘ │
│ │ │
│ 5. VALIDACIÓN │
│ ┌────────▼────────┐ │
│ │ Monto OCR vs │ │
│ │ Monto mensaje │ │
│ │ (tolerancia 5%) │ │
│ └────────┬────────┘ │
│ │ │
│ 6. REGISTRO │
│ ┌────────▼────────┐ ┌─────────────────┐ │
│ │ NocoDB API │────►│ Vendedor │ │
│ │ │ │ Venta │ │
│ │ │ │ Detalle │ │
│ │ │ │ Meta │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ 7. COMISIONES │
│ ┌────────▼────────┐ ┌─────────────────┐ │
│ │ Calcular │────►│ tubos > 3? │ │
│ │ comisión │ │ comisión = $10 │ │
│ │ │ │ × (tubos - 3) │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ 8. RESPUESTA │
│ ┌────────▼────────┐ │
│ │ Mensaje en │ │
│ │ Mattermost + │ │
│ │ Reacción ✓ │ │
│ └─────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────────────┘
```
## Seguridad
```
┌─────────────────────────────────────────────────────────────────┐
│ CAPA DE SEGURIDAD │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Mattermost Auth │ │ NocoDB Auth │ │
│ │ ───────────────── │ │ ───────────────── │ │
│ │ Bot Token │ │ API Token (JWT) │ │
│ │ Webhook Secret │ │ Bearer Auth │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Contenedor Sales Bot │ │
│ │ ───────────────────────────────────────────────── │ │
│ │ Usuario: salesbot (no-root) │ │
│ │ Filesystem: read-only donde es posible │ │
│ │ Red: bridge aislada │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## Escalabilidad
```
┌─────────────────────┐
│ Load Balancer │
│ (futuro) │
└──────────┬──────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Sales Bot │ │ Sales Bot │ │ Sales Bot │
│ Worker 1 │ │ Worker 2 │ │ Worker 3 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────┼───────────────────┘
┌──────────▼──────────┐
│ NocoDB │
│ (PostgreSQL) │
└─────────────────────┘
```
El sistema está preparado para escalar horizontalmente gracias a:
- Gunicorn con múltiples workers
- Base de datos centralizada
- Contenedores stateless

296
docs/INSTALACION.md Normal file
View File

@@ -0,0 +1,296 @@
# Guía de Instalación
## Requisitos del Sistema
### Hardware Mínimo
- CPU: 2 cores
- RAM: 4 GB
- Disco: 20 GB
### Software
- Docker 20.10+
- Docker Compose 2.0+
- Git
### Red
- Puerto 5000 (Sales Bot)
- Puerto 8065 (Mattermost)
- Puerto 8080 (NocoDB)
---
## Instalación Paso a Paso
### 1. Clonar el Repositorio
```bash
git clone https://git.consultoria-as.com/<usuario>/stacks.git
cd stacks
```
### 2. Configurar Variables de Entorno
Copiar el archivo de ejemplo y editar:
```bash
cd sales-bot
cp .env.example .env
nano .env
```
Configurar las siguientes variables:
```env
# Mattermost
MATTERMOST_URL=http://<IP>:8065
MATTERMOST_BOT_TOKEN=<tu_token>
MATTERMOST_TEAM_NAME=sales
MATTERMOST_WEBHOOK_SECRET=<tu_secret>
MATTERMOST_WEBHOOK_URL=http://<IP>:8065/hooks/<hook_id>
# NocoDB
NOCODB_URL=http://<IP>:8080
NOCODB_TOKEN=<tu_token>
NOCODB_TABLE_VENDEDORES=<table_id>
NOCODB_TABLE_VENTAS=<table_id>
NOCODB_TABLE_VENTAS_DETALLE=<table_id>
NOCODB_TABLE_METAS=<table_id>
# Flask
FLASK_PORT=5000
LOG_LEVEL=INFO
TZ_OFFSET=-6
```
### 3. Iniciar Mattermost
```bash
cd ../mattermost
docker compose up -d
```
Esperar a que inicie completamente:
```bash
docker compose logs -f
# Esperar mensaje "Server is listening"
```
Acceder a http://<IP>:8065 y completar la configuración inicial:
1. Crear cuenta de administrador
2. Crear team "sales"
3. Crear canales necesarios
### 4. Configurar Bot en Mattermost
1. Ir a **Integraciones > Bot Accounts**
2. Crear nuevo bot:
- Username: `salesbot`
- Copiar el token generado
3. Ir a **Integraciones > Outgoing Webhooks**
4. Crear webhook:
- Canal: canal de ventas
- URL: `http://<IP>:5000/webhook/mattermost`
- Copiar el token
5. Ir a **Integraciones > Slash Commands**
6. Crear comandos:
- `/metas``http://<IP>:5000/comando/metas`
- `/ranking``http://<IP>:5000/comando/ranking`
### 5. Iniciar NocoDB
```bash
cd ../nocodb
docker compose up -d
```
Acceder a http://<IP>:8080 y:
1. Crear cuenta
2. Crear base de datos
3. Crear tablas (ver estructura en docs/ARQUITECTURA.md)
4. Obtener API token en Settings
### 6. Configurar Tablas en NocoDB
Crear las siguientes tablas:
**Vendedores:**
```
- Id (Auto)
- username (Text, Required)
- nombre_completo (Text)
- email (Email)
- meta_diaria_tubos (Number, Default: 3)
- activo (Checkbox, Default: true)
- fecha_registro (DateTime)
```
**Ventas:**
```
- Id (Auto)
- vendedor_username (Text, Required)
- monto (Currency)
- cliente (Text)
- fecha_venta (DateTime)
- estado (SingleSelect: pendiente, completada, cancelada)
- canal (Text)
- mensaje_id (Text)
- imagen_ticket (Attachment)
- descripcion (LongText)
```
**Ventas Detalle:**
```
- Id (Auto)
- venta_id (Number)
- producto (Text)
- marca (Text)
- cantidad (Number)
- precio_unitario (Currency)
- importe (Currency)
```
**Metas:**
```
- Id (Auto)
- vendedor_username (Text)
- mes (Text)
- tubos_vendidos (Number)
- comision (Currency)
- meta_diaria (Number)
- porcentaje_completado (Percent)
- total_vendido (Currency)
```
### 7. Obtener IDs de Tablas
En NocoDB, para cada tabla:
1. Abrir la tabla
2. Copiar el ID de la URL: `/table/<TABLE_ID>`
Actualizar estos IDs en el archivo `.env`.
### 8. Iniciar Sales Bot
```bash
cd ../sales-bot
docker compose up -d
```
Verificar que esté corriendo:
```bash
curl http://localhost:5000/health
```
---
## Verificación de la Instalación
### 1. Verificar Servicios
```bash
# Mattermost
curl http://localhost:8065/api/v4/system/ping
# NocoDB
curl http://localhost:8080/api/v2/health
# Sales Bot
curl http://localhost:5000/health
```
### 2. Probar Flujo Completo
1. Enviar mensaje en Mattermost:
```
venta @monto 100 @cliente Prueba
```
2. Verificar que el bot responda
3. Verificar registro en NocoDB
---
## Solución de Problemas
### Sales Bot no responde
```bash
# Ver logs
docker compose -f sales-bot/compose.yaml logs -f
# Reiniciar
docker compose -f sales-bot/compose.yaml restart
```
### Error de conexión a Mattermost
Verificar:
1. Token del bot es correcto
2. Bot tiene permisos en el canal
3. URL de Mattermost es accesible
### Error de conexión a NocoDB
Verificar:
1. Token de API es correcto
2. IDs de tablas son correctos
3. URL de NocoDB es accesible
### OCR no funciona
Verificar:
1. Tesseract está instalado en el contenedor
2. Imagen es legible
3. Ver logs para errores específicos
---
## Actualización
```bash
# Detener servicios
docker compose -f sales-bot/compose.yaml down
docker compose -f mattermost/compose.yaml down
docker compose -f nocodb/compose.yaml down
# Actualizar código
git pull
# Reconstruir imágenes
docker compose -f sales-bot/compose.yaml build
# Iniciar servicios
docker compose -f mattermost/compose.yaml up -d
docker compose -f nocodb/compose.yaml up -d
docker compose -f sales-bot/compose.yaml up -d
```
---
## Backup y Restauración
### Backup
```bash
# Backup de Mattermost
docker compose -f mattermost/compose.yaml exec postgres \
pg_dump -U consultoria-as mattermost > mattermost_backup.sql
# Backup de NocoDB
docker compose -f nocodb/compose.yaml exec postgres \
pg_dump -U consultoria-as nocodb > nocodb_backup.sql
```
### Restauración
```bash
# Restaurar Mattermost
cat mattermost_backup.sql | docker compose -f mattermost/compose.yaml exec -T postgres \
psql -U consultoria-as mattermost
# Restaurar NocoDB
cat nocodb_backup.sql | docker compose -f nocodb/compose.yaml exec -T postgres \
psql -U consultoria-as nocodb
```