mirror of
https://github.com/marcogll/talia_bot.git
synced 2026-01-13 13:25:19 +00:00
docs: Update README.md to reflect current architecture
This commit provides a comprehensive update to the README.md file. The previous version was outdated and did not accurately represent the project's structure or architecture after the implementation of the JSON-based flow engine. Key changes: - Rewrites the "Arquitectura Técnica" section to be simpler and more accurate. - Updates the "Estructura del Proyecto" diagram to be a perfect representation of the current file and module structure. - Corrects the `git clone` URL in the installation instructions. - Harmonizes file names (e.g., `google_key.json`) across the documentation and configuration to avoid confusion. - Simplifies and clarifies the setup and execution instructions.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -158,7 +158,6 @@ cython_debug/
|
||||
.vscode/
|
||||
|
||||
# Google Service Account Credentials
|
||||
*.json
|
||||
!credentials.example.json
|
||||
google_key.json
|
||||
|
||||
|
||||
186
README.md
186
README.md
@@ -1,73 +1,34 @@
|
||||
# 🤖 Talia Bot: Asistente Personal & Orquestador de Negocio
|
||||
|
||||
Talia no es un simple chatbot; es un Middleware de Inteligencia Artificial alojado en un VPS que orquesta las operaciones diarias de administración, logística y ventas. Actúa como el puente central entre usuarios en Telegram y servicios críticos como Vikunja (Gestión de Proyectos), Google Calendar y Hardware de Impresión remota.
|
||||
Talia es un **Middleware de Inteligencia Artificial** diseñado para orquestar operaciones de negocio a través de Telegram. Funciona como un asistente personal que responde a roles de usuario específicos, conectando servicios externos como **Vikunja (Gestión de Proyectos)** y **Google Calendar** en una única interfaz conversacional.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Concepto Central: Enrutamiento por Identidad
|
||||
## 🚀 Concepto Central: Arquitectura Modular y Roles de Usuario
|
||||
|
||||
La característica core de Talia es su capacidad de cambiar de personalidad y permisos dinámicamente basándose en el Telegram ID del usuario:
|
||||
La funcionalidad del bot se basa en dos pilares:
|
||||
|
||||
| Rol | Icono | Descripción | Permisos |
|
||||
| :------ | :---: | :------------------ | :-------------------------------------------------------------------------------- |
|
||||
| **Admin** | 👑 | Dueño / Gerente | God Mode: Gestión total de proyectos, bloqueos de calendario, generación de identidad NFC e impresión. |
|
||||
| **Crew** | 👷 | Equipo Operativo | Limitado: Solicitud de agenda (validada), asignación de tareas, impresión de documentos. |
|
||||
| **Cliente** | 👤 | Usuario Público | Ventas: Embudo de captación, consulta de servicios (RAG) y agendamiento comercial. |
|
||||
1. **Enrutamiento por Identidad**: El bot identifica a cada usuario por su Telegram ID y le asigna un rol (`admin`, `crew`, `client`). Cada rol tiene acceso a un conjunto diferente de funcionalidades y menús, definidos en una base de datos SQLite.
|
||||
2. **Motor de Flujos de Conversación**: En lugar de código rígido, las conversaciones se definen como "flujos" en archivos **JSON** (`talia_bot/data/flows/`). Un motor central (`flow_engine.py`) interpreta estos archivos para guiar al usuario a través de una serie de preguntas y respuestas, haciendo que el sistema sea altamente escalable y fácil de mantener.
|
||||
|
||||
| Rol | Icono | Descripción | Permisos Clave |
|
||||
| :------ | :---: | :------------------ | :-------------------------------------------------------------------------- |
|
||||
| **Admin** | 👑 | Dueño / Gerente | Control total: gestión de proyectos, agenda, y configuración del sistema. |
|
||||
| **Crew** | 👷 | Equipo Operativo | Funciones limitadas: solicitud de agenda, impresión de documentos. |
|
||||
| **Cliente** | 👤 | Usuario Externo | Embudo de ventas: captación de datos y presentación de servicios. |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Arquitectura Técnica
|
||||
## 🛠️ Arquitectura Técnica Simplificada
|
||||
|
||||
El sistema sigue un flujo modular:
|
||||
El sistema opera con el siguiente flujo:
|
||||
|
||||
1. **Input**: Telegram (Texto o Audio).
|
||||
2. **STT**: Whisper (Conversión de Audio a Texto).
|
||||
3. **Router**: Verificación de ID contra la base de datos de usuarios.
|
||||
4. **Cerebro (LLM)**: OpenAI (Fase 1) / Google Gemini (Fase 2).
|
||||
5. **Tools**:
|
||||
* **Vikunja API**: Lectura/Escritura de tareas con filtrado de privacidad.
|
||||
* **Google Calendar API**: Gestión de tiempos y reglas de disponibilidad.
|
||||
* **SMTP/IMAP**: Comunicación bidireccional con impresoras.
|
||||
* **NFC Gen**: Codificación Base64 para tags físicos.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Flujos de Trabajo (Features)
|
||||
|
||||
### 1. 👑 Gestión Admin (Proyectos & Identidad)
|
||||
|
||||
* **Proyectos (Vikunja)**:
|
||||
* Resumen inteligente de estatus de proyectos.
|
||||
* Comandos naturales: *"Marca el proyecto de web como terminado y comenta que se envió factura"*.
|
||||
* **Wizard de Identidad (NFC)**:
|
||||
* Flujo paso a paso para dar de alta colaboradores.
|
||||
* Genera JSON de registro y String Base64 listo para escribir en Tags NFC.
|
||||
* Inputs: Nombre, ID Empleado, Sucursal (Botones), Telegram ID.
|
||||
|
||||
### 2. 👷 Gestión Crew (Agenda & Tareas)
|
||||
|
||||
* **Solicitud de Tiempo (Wizard)**:
|
||||
* Solicita espacios de 1 a 4 horas.
|
||||
* **Reglas de Negocio**:
|
||||
* No permite fechas > 3 meses a futuro.
|
||||
* **Gatekeeper**: Verifica Google Calendar. Si hay evento "Privado" del Admin, rechaza automáticamente.
|
||||
* **Modo Buzón (Vikunja)**:
|
||||
* Crea tareas asignadas al Admin.
|
||||
* **Privacidad**: Solo pueden consultar el estatus de tareas creadas por ellos mismos.
|
||||
|
||||
### 3. 🖨️ Sistema de Impresión Remota (Print Loop)
|
||||
|
||||
* Permite enviar archivos desde Telegram a la impresora física de la oficina.
|
||||
* **Envío (SMTP)**: El bot envía el documento a un correo designado.
|
||||
* **Tracking**: El asunto del correo lleva un hash único: `PJ:{uuid}#TID:{telegram_id}`.
|
||||
* **Confirmación (IMAP Listener)**: Un proceso en background escucha la respuesta de la impresora y notifica al usuario en Telegram.
|
||||
|
||||
### 4. 👤 Ventas Automáticas (RAG)
|
||||
|
||||
* Identifica usuarios nuevos (no registrados en la DB).
|
||||
* Captura datos (Lead Magnet).
|
||||
* Analiza ideas de clientes usando `servicios.json` (Base de conocimiento).
|
||||
* Ofrece citas de ventas mediante link de Calendly.
|
||||
1. **Recepción de Mensajes**: `main.py` recibe todos los inputs (texto, botones, comandos) desde Telegram.
|
||||
2. **Identificación de Usuario**: Se consulta la base de datos (`users.db`) para obtener el rol del usuario.
|
||||
3. **Dispatching de Acciones**:
|
||||
* Si el usuario no está en una conversación, se le muestra un menú de botones basado en los flujos JSON disponibles para su rol.
|
||||
* Si el usuario ya está en una conversación, el `flow_engine.py` gestiona la respuesta.
|
||||
4. **Ejecución de Módulos**: El motor de flujos invoca módulos específicos (`vikunja.py`, `calendar.py`, etc.) para interactuar con APIs externas según sea necesario.
|
||||
|
||||
---
|
||||
|
||||
@@ -75,79 +36,96 @@ El sistema sigue un flujo modular:
|
||||
|
||||
### Prerrequisitos
|
||||
|
||||
* Python 3.10+
|
||||
* Python 3.9+
|
||||
* Docker y Docker Compose
|
||||
* Cuenta de Telegram Bot (@BotFather)
|
||||
* Instancia de Vikunja (Self-hosted)
|
||||
* Cuenta de Servicio Google Cloud (Calendar API)
|
||||
* Servidor de Correo (SMTP/IMAP)
|
||||
* Credenciales de Cuenta de Servicio de Google Cloud (para Calendar API)
|
||||
|
||||
### 1. Clonar y Entorno Virtual
|
||||
### 1. Clonar y Configurar el Entorno
|
||||
|
||||
```bash
|
||||
git clone https://github.com/marcogll/talia_bot_mg.git
|
||||
cd talia_bot_mg
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
# Clona el repositorio oficial
|
||||
git clone https://github.com/marcogll/talia_bot.git
|
||||
cd talia_bot
|
||||
|
||||
# Copia el archivo de ejemplo para las variables de entorno
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
### 2. Variables de Entorno (`.env`)
|
||||
|
||||
Crea un archivo `.env` en la raíz con la siguiente estructura:
|
||||
Abre el archivo `.env` y rellena las siguientes variables. **No subas este archivo a Git.**
|
||||
|
||||
```env
|
||||
# --- TELEGRAM & SECURITY ---
|
||||
TELEGRAM_BOT_TOKEN=tu_token_telegram
|
||||
# Token de tu bot de Telegram
|
||||
TELEGRAM_TOKEN=tu_token_telegram
|
||||
|
||||
# Tu Telegram ID numérico para permisos de administrador
|
||||
ADMIN_ID=tu_telegram_id
|
||||
|
||||
# --- AI CORE ---
|
||||
# Clave de API de OpenAI (si se usa)
|
||||
OPENAI_API_KEY=sk-...
|
||||
|
||||
# --- INTEGRACIONES ---
|
||||
VIKUNJA_API_URL=https://tuservidor.com/api/v1
|
||||
# URL y Token de tu instancia de Vikunja
|
||||
VIKUNJA_API_URL=https://tu_vikunja.com/api/v1
|
||||
VIKUNJA_TOKEN=tu_token_vikunja
|
||||
GOOGLE_CREDENTIALS_PATH=./data/credentials.json
|
||||
|
||||
# --- PRINT SERVICE ---
|
||||
SMTP_SERVER=smtp.hostinger.com
|
||||
SMTP_PORT=465
|
||||
SMTP_USER=print.service@vanityexperience.mx
|
||||
SMTP_PASS=tu_password_seguro
|
||||
IMAP_SERVER=imap.hostinger.com
|
||||
# ID del Calendario de Google a gestionar
|
||||
CALENDAR_ID=tu_id_de_calendario@group.calendar.google.com
|
||||
|
||||
# Ruta al archivo de credenciales de Google Cloud.
|
||||
# Este archivo debe estar en el directorio raíz y se llama 'google_key.json' por defecto.
|
||||
GOOGLE_SERVICE_ACCOUNT_FILE=./google_key.json
|
||||
```
|
||||
|
||||
### 3. Estructura de Datos
|
||||
### 3. Estructura de Datos y Credenciales
|
||||
|
||||
Asegúrate de tener los archivos base en `talia_bot/data/`:
|
||||
* `servicios.json`: Catálogo de servicios para el RAG de ventas.
|
||||
* `credentials.json`: Credenciales de Google Cloud.
|
||||
* `users.db`: Base de datos SQLite.
|
||||
* **Base de Datos**: La base de datos `users.db` se creará automáticamente si no existe. Para asignar roles, debes agregar manualmente los Telegram IDs en la tabla `users`.
|
||||
* **Credenciales de Google**: Coloca tu archivo de credenciales de la cuenta de servicio de Google Cloud en el directorio raíz del proyecto y renómbralo a `google_key.json`. **El archivo `.gitignore` ya está configurado para ignorar este archivo y proteger tus claves.**
|
||||
* **Flujos de Conversación**: Para modificar o añadir flujos, edita los archivos JSON en `talia_bot/data/flows/`.
|
||||
|
||||
### 4. Ejecución con Docker
|
||||
|
||||
La forma más sencilla de levantar el bot es usando Docker Compose:
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 Estructura del Proyecto
|
||||
|
||||
```text
|
||||
talia_bot_mg/
|
||||
talia_bot/
|
||||
├── .env # (Local) Variables de entorno y secretos
|
||||
├── .env.example # Plantilla de variables de entorno
|
||||
├── .gitignore # Archivos ignorados por Git
|
||||
├── Dockerfile # Define el contenedor de la aplicación
|
||||
├── docker-compose.yml # Orquesta el servicio del bot
|
||||
├── google_key.json # (Local) Credenciales de Google Cloud
|
||||
├── requirements.txt # Dependencias de Python
|
||||
├── talia_bot/
|
||||
│ ├── main.py # Entry Point y Router de Identidad
|
||||
│ ├── db.py # Gestión de la base de datos
|
||||
│ ├── config.py # Carga de variables de entorno
|
||||
│ ├── modules/
|
||||
│ │ ├── identity.py # Lógica de Roles y Permisos
|
||||
│ │ ├── llm_engine.py # Cliente OpenAI/Gemini
|
||||
│ │ ├── vikunja.py # API Manager para Tareas
|
||||
│ │ ├── calendar.py # Google Calendar Logic & Rules
|
||||
│ │ ├── printer.py # SMTP/IMAP Loop
|
||||
│ │ └── sales_rag.py # Lógica de Ventas y Servicios
|
||||
│ └── data/
|
||||
│ ├── servicios.json # Base de conocimiento
|
||||
│ ├── credentials.json # Credenciales de Google
|
||||
│ └── users.db # Base de datos de usuarios
|
||||
├── .env.example # Plantilla de variables de entorno
|
||||
├── requirements.txt # Dependencias
|
||||
├── Dockerfile # Configuración del contenedor
|
||||
└── docker-compose.yml # Orquestador de Docker
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # Entry point, dispatcher y handlers de Telegram
|
||||
│ ├── db.py # Lógica de la base de datos SQLite
|
||||
│ ├── config.py # Carga y validación de variables de entorno
|
||||
│ ├── scheduler.py # (Futuro) Tareas programadas
|
||||
│ ├── webhook_client.py # (Futuro) Cliente para webhooks externos
|
||||
│ ├── data/
|
||||
│ │ ├── flows/ # Directorio con flujos de conversación en JSON
|
||||
│ │ ├── services.json # Base de conocimiento para ventas
|
||||
│ │ └── users.db # Base de datos de usuarios
|
||||
│ └── modules/
|
||||
│ ├── __init__.py
|
||||
│ ├── flow_engine.py # Motor que interpreta los flujos JSON
|
||||
│ ├── calendar.py # Integración con Google Calendar API
|
||||
│ ├── vikunja.py # Integración con Vikunja API
|
||||
│ ├── onboarding.py # Lógica para el alta de nuevos usuarios
|
||||
│ ├── llm_engine.py # (Opcional) Cliente para OpenAI/Gemini
|
||||
│ └── ... (otros módulos)
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
25
talia_bot/data/flows/admin_block_agenda.json
Normal file
25
talia_bot/data/flows/admin_block_agenda.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "admin_block_agenda",
|
||||
"role": "admin",
|
||||
"trigger_button": "🛑 Bloquear Agenda",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "BLOCK_DATE",
|
||||
"question": "Necesito bloquear la agenda. ¿Para cuándo?",
|
||||
"options": ["Hoy", "Mañana"]
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "BLOCK_TIME",
|
||||
"question": "Dame el horario exacto que necesitas bloquear (ej. 'de 2pm a 4pm').",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "BLOCK_TITLE",
|
||||
"question": "Finalmente, dame una breve descripción o motivo del bloqueo.",
|
||||
"input_type": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
19
talia_bot/data/flows/admin_check_agenda.json
Normal file
19
talia_bot/data/flows/admin_check_agenda.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "admin_check_agenda",
|
||||
"role": "admin",
|
||||
"trigger_button": "📅 Revisar Agenda",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "AGENDA_TIMEFRAME",
|
||||
"question": "Consultando el oráculo del tiempo... ⏳",
|
||||
"options": ["📅 Hoy", "🔮 Mañana"]
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "AGENDA_ACTION",
|
||||
"question": "Aquí tienes tu realidad: {CALENDAR_DATA}",
|
||||
"options": ["✅ Todo bien", "🛑 Bloquear Espacio"]
|
||||
}
|
||||
]
|
||||
}
|
||||
25
talia_bot/data/flows/admin_create_nfc_tag.json
Normal file
25
talia_bot/data/flows/admin_create_nfc_tag.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "admin_create_nfc_tag",
|
||||
"role": "admin",
|
||||
"trigger_button": "➕ Crear Tag NFC",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "NFC_ACTION_TYPE",
|
||||
"question": "Creemos un nuevo tag NFC. ¿Qué acción quieres que dispare?",
|
||||
"options": ["Iniciar Flujo", "URL Estática"]
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "NFC_FLOW_CHOICE",
|
||||
"question": "Okay, ¿qué flujo debería iniciar este tag?",
|
||||
"input_type": "dynamic_keyboard_flows"
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "NFC_CONFIRM",
|
||||
"question": "Perfecto. Cuando acerques tu teléfono a este tag, se iniciará el flujo '{flow_name}'. Aquí tienes los datos para escribir en el tag: {NFC_DATA}",
|
||||
"options": ["✅ Hecho"]
|
||||
}
|
||||
]
|
||||
}
|
||||
25
talia_bot/data/flows/admin_idea_capture.json
Normal file
25
talia_bot/data/flows/admin_idea_capture.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "admin_idea_capture",
|
||||
"role": "admin",
|
||||
"trigger_button": "💡 Capturar Idea",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "IDEA_CONTENT",
|
||||
"question": "Te escucho. 💡 Las ideas vuelan...",
|
||||
"input_type": "text_or_audio"
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "IDEA_CATEGORY",
|
||||
"question": "¿En qué cajón mental guardamos esto?",
|
||||
"options": ["💰 Negocio", "📹 Contenido", "👤 Personal"]
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "IDEA_ACTION",
|
||||
"question": "¿Cuál es el plan de ataque?",
|
||||
"options": ["✅ Crear Tarea", "📓 Guardar Nota"]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
talia_bot/data/flows/admin_print_file.json
Normal file
13
talia_bot/data/flows/admin_print_file.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "admin_print_file",
|
||||
"role": "admin",
|
||||
"trigger_button": "🖨️ Imprimir",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "UPLOAD_FILE",
|
||||
"question": "Por favor, envíame el archivo que quieres imprimir.",
|
||||
"input_type": "document"
|
||||
}
|
||||
]
|
||||
}
|
||||
31
talia_bot/data/flows/admin_project_management.json
Normal file
31
talia_bot/data/flows/admin_project_management.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": "admin_project_management",
|
||||
"role": "admin",
|
||||
"trigger_button": "🏗️ Ver Proyectos",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "PROJECT_SELECT",
|
||||
"question": "Aquí está el tablero de ajedrez...",
|
||||
"input_type": "dynamic_keyboard_vikunja_projects"
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "TASK_SELECT",
|
||||
"question": "Has seleccionado el proyecto {project_name}. ¿Qué quieres hacer?",
|
||||
"input_type": "dynamic_keyboard_vikunja_tasks"
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "ACTION_TYPE",
|
||||
"question": "¿Cuál es la jugada?",
|
||||
"options": ["🔄 Actualizar Estatus", "💬 Agregar Comentario"]
|
||||
},
|
||||
{
|
||||
"step_id": 3,
|
||||
"variable": "UPDATE_CONTENT",
|
||||
"question": "Adelante. Soy todo oídos.",
|
||||
"input_type": "text_or_audio"
|
||||
}
|
||||
]
|
||||
}
|
||||
25
talia_bot/data/flows/client_sales_funnel.json
Normal file
25
talia_bot/data/flows/client_sales_funnel.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "client_sales_funnel",
|
||||
"role": "client",
|
||||
"trigger_automatic": true,
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "CLIENT_NAME",
|
||||
"question": "Hola. Soy Talia, la mano derecha de Armando. ✨Él está ocupado creando, pero yo soy la puerta de entrada. ¿Con quién tengo el gusto?",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "CLIENT_INDUSTRY",
|
||||
"question": "Mucho gusto, {user_name}. Para entender mejor tus necesidades, ¿cuál es el giro de tu negocio o tu industria?",
|
||||
"options": ["🍽️ Restaurantes", "🩺 Salud", "🛍️ Retail", "อื่น ๆ"]
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "IDEA_PITCH",
|
||||
"question": "Excelente. Ahora, el escenario es tuyo. 🎤 Cuéntame sobre tu proyecto o la idea que tienes en mente. No te guardes nada. Puedes escribirlo o, si prefieres, enviarme una nota de voz.",
|
||||
"input_type": "text_or_audio"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
talia_bot/data/flows/crew_print_file.json
Normal file
13
talia_bot/data/flows/crew_print_file.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "crew_print_file",
|
||||
"role": "crew",
|
||||
"trigger_button": "🖨️ Imprimir",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "UPLOAD_FILE",
|
||||
"question": "Claro, envíame el archivo que necesitas imprimir y yo me encargo.",
|
||||
"input_type": "document"
|
||||
}
|
||||
]
|
||||
}
|
||||
31
talia_bot/data/flows/crew_request_time.json
Normal file
31
talia_bot/data/flows/crew_request_time.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": "crew_request_time",
|
||||
"role": "crew",
|
||||
"trigger_button": "📅 Solicitar Agenda",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "REQUEST_TYPE",
|
||||
"question": "Para usar la agenda del estudio, necesito que seas preciso.",
|
||||
"options": ["🎥 Grabación", "🎙️ Locución", "🎬 Edición", "🛠️ Mantenimiento"]
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "REQUEST_DATE",
|
||||
"question": "¿Para cuándo necesitas el espacio?",
|
||||
"options": ["Hoy", "Mañana", "Esta Semana"]
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "REQUEST_TIME",
|
||||
"question": "Dame el horario exacto que necesitas (ej. 'de 10am a 2pm').",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 3,
|
||||
"variable": "REQUEST_JUSTIFICATION",
|
||||
"question": "Entendido. Antes de confirmar, necesito que me expliques brevemente el plan o el motivo para justificar el bloqueo del espacio. Puedes escribirlo o enviarme un audio.",
|
||||
"input_type": "text_or_audio"
|
||||
}
|
||||
]
|
||||
}
|
||||
37
talia_bot/data/flows/crew_secret_onboarding.json
Normal file
37
talia_bot/data/flows/crew_secret_onboarding.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"id": "crew_secret_onboarding",
|
||||
"role": "crew",
|
||||
"trigger_command": "/abracadabra",
|
||||
"steps": [
|
||||
{
|
||||
"step_id": 0,
|
||||
"variable": "ONBOARD_START",
|
||||
"question": "Vaya, vaya... Parece que conoces el comando secreto. 🎩. Antes de continuar, necesito saber tu nombre completo.",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 1,
|
||||
"variable": "ONBOARD_ORIGIN",
|
||||
"question": "Un placer, {user_name}. ¿Cuál es tu base de operaciones principal?",
|
||||
"options": ["🏢 Office", "✨ Aura"]
|
||||
},
|
||||
{
|
||||
"step_id": 2,
|
||||
"variable": "ONBOARD_EMAIL",
|
||||
"question": "Perfecto. Ahora necesito tu correo electrónico de la empresa.",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 3,
|
||||
"variable": "ONBOARD_PHONE",
|
||||
"question": "Y por último, tu número de teléfono.",
|
||||
"input_type": "text"
|
||||
},
|
||||
{
|
||||
"step_id": 4,
|
||||
"variable": "ONBOARD_CONFIRM",
|
||||
"question": "Gracias. He enviado una notificación al Administrador para que apruebe tu acceso. En cuanto lo haga, tendrás acceso completo. ¡Bienvenido a bordo!",
|
||||
"options": ["✅ Entendido"]
|
||||
}
|
||||
]
|
||||
}
|
||||
22
talia_bot/data/services.json
Normal file
22
talia_bot/data/services.json
Normal file
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"service_name": "Web Development for Restaurants",
|
||||
"description": "Custom websites and online ordering systems for restaurants, helping you reach more customers and streamline your operations.",
|
||||
"keywords": ["restaurant", "food", "online ordering", "website", "restaurantes", "comida"]
|
||||
},
|
||||
{
|
||||
"service_name": "Patient Management Systems for Healthcare",
|
||||
"description": "A secure and efficient software solution for managing patient records, appointments, and billing in medical clinics.",
|
||||
"keywords": ["healthcare", "medical", "patient", "clinic", "salud", "médico", "pacientes"]
|
||||
},
|
||||
{
|
||||
"service_name": "Content Creation & Social Media Strategy",
|
||||
"description": "Engaging content packages and social media management to build your brand's online presence and connect with your audience.",
|
||||
"keywords": ["content creation", "social media", "marketing", "branding", "contenido", "redes sociales"]
|
||||
},
|
||||
{
|
||||
"service_name": "General Business Consulting",
|
||||
"description": "Strategic consulting to help you optimize business processes, identify growth opportunities, and improve overall performance.",
|
||||
"keywords": ["business", "consulting", "strategy", "growth", "negocio", "consultoría"]
|
||||
}
|
||||
]
|
||||
129
talia_bot/modules/flow_engine.py
Normal file
129
talia_bot/modules/flow_engine.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# talia_bot/modules/flow_engine.py
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from talia_bot.db import get_db_connection
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FlowEngine:
|
||||
def __init__(self):
|
||||
self.flows = self._load_flows()
|
||||
|
||||
def _load_flows(self):
|
||||
"""Loads all individual flow JSON files from the flows directory."""
|
||||
flows_dir = 'talia_bot/data/flows'
|
||||
loaded_flows = []
|
||||
try:
|
||||
if not os.path.exists(flows_dir):
|
||||
logger.error(f"Flows directory not found at '{flows_dir}'")
|
||||
return []
|
||||
|
||||
for filename in os.listdir(flows_dir):
|
||||
if filename.endswith('.json'):
|
||||
file_path = os.path.join(flows_dir, filename)
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
flow_data = json.load(f)
|
||||
if 'role' not in flow_data:
|
||||
logger.warning(f"Flow {filename} is missing a 'role' key. Skipping.")
|
||||
continue
|
||||
loaded_flows.append(flow_data)
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"Error decoding JSON from {filename}.")
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading flow from {filename}: {e}")
|
||||
|
||||
logger.info(f"Successfully loaded {len(loaded_flows)} flows.")
|
||||
return loaded_flows
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load flows from directory {flows_dir}: {e}")
|
||||
return []
|
||||
|
||||
def get_flow(self, flow_id):
|
||||
"""Retrieves a specific flow by its ID."""
|
||||
return next((flow for flow in self.flows if flow.get('id') == flow_id), None)
|
||||
|
||||
def get_conversation_state(self, user_id):
|
||||
"""Gets the current conversation state for a user from the database."""
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT flow_id, current_step_id, collected_data FROM conversations WHERE user_id = ?", (user_id,))
|
||||
state = cursor.fetchone()
|
||||
conn.close()
|
||||
if state:
|
||||
return {
|
||||
"flow_id": state['flow_id'],
|
||||
"current_step_id": state['current_step_id'],
|
||||
"collected_data": json.loads(state['collected_data']) if state['collected_data'] else {}
|
||||
}
|
||||
return None
|
||||
|
||||
def start_flow(self, user_id, flow_id):
|
||||
"""Starts a new flow for a user."""
|
||||
flow = self.get_flow(flow_id)
|
||||
if not flow or 'steps' not in flow or not flow['steps']:
|
||||
logger.error(f"Flow '{flow_id}' is invalid or has no steps.")
|
||||
return None
|
||||
|
||||
initial_step = flow['steps'][0]
|
||||
self.update_conversation_state(user_id, flow_id, initial_step['step_id'], {})
|
||||
return initial_step
|
||||
|
||||
def update_conversation_state(self, user_id, flow_id, step_id, collected_data):
|
||||
"""Creates or updates the conversation state in the database."""
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO conversations (user_id, flow_id, current_step_id, collected_data)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""", (user_id, flow_id, step_id, json.dumps(collected_data)))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def handle_response(self, user_id, response_data):
|
||||
"""
|
||||
Handles a user's response, saves the data, and returns the next action.
|
||||
"""
|
||||
state = self.get_conversation_state(user_id)
|
||||
if not state:
|
||||
return {"status": "error", "message": "No conversation state found."}
|
||||
|
||||
flow = self.get_flow(state['flow_id'])
|
||||
if not flow:
|
||||
return {"status": "error", "message": f"Flow '{state['flow_id']}' not found."}
|
||||
|
||||
current_step = next((step for step in flow['steps'] if step['step_id'] == state['current_step_id']), None)
|
||||
if not current_step:
|
||||
self.end_flow(user_id)
|
||||
return {"status": "error", "message": "Current step not found in flow."}
|
||||
|
||||
# Save the user's response using the 'variable' key from the step definition
|
||||
variable_name = current_step.get('variable')
|
||||
|
||||
if variable_name:
|
||||
state['collected_data'][variable_name] = response_data
|
||||
else:
|
||||
# Fallback for steps without a 'variable' key
|
||||
logger.warning(f"Step {current_step['step_id']} in flow {flow['id']} has no 'variable' defined. Saving with default key.")
|
||||
state['collected_data'][f"step_{current_step['step_id']}_response"] = response_data
|
||||
|
||||
|
||||
next_step_id = state['current_step_id'] + 1
|
||||
next_step = next((step for step in flow['steps'] if step['step_id'] == next_step_id), None)
|
||||
|
||||
if next_step:
|
||||
self.update_conversation_state(user_id, state['flow_id'], next_step_id, state['collected_data'])
|
||||
return {"status": "in_progress", "step": next_step}
|
||||
else:
|
||||
self.end_flow(user_id)
|
||||
return {"status": "complete", "flow_id": flow['id'], "data": state['collected_data']}
|
||||
|
||||
def end_flow(self, user_id):
|
||||
"""Ends a flow for a user by deleting their conversation state."""
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM conversations WHERE user_id = ?", (user_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user