Merge pull request #3 from marcogll/feat/docker-mysql-logging-13516104967243096474

Feat/docker mysql logging 13516104967243096474
This commit is contained in:
Marco Gallegos
2025-12-13 21:50:09 -06:00
committed by GitHub
5 changed files with 119 additions and 102 deletions

6
.env
View File

@@ -3,8 +3,4 @@ TELEGRAM_TOKEN=TU_TOKEN_NUEVO_AQUI
# Webhooks de n8n (puedes agregar más aquí en el futuro)
WEBHOOK_CONTRATO=https://flows.soul23.cloud/webhook/DuXh9Oi7SCAMf9
WEBHOOK_PRINT=
WEBHOOK_VACACIONES=
# --- DATABASE ---
DATABASE_URL=mysql+mysqlconnector://user:password@db:3306/vanessa_logs
# WEBHOOK_VACACIONES=https://... (futuro)

22
.env.example Normal file
View File

@@ -0,0 +1,22 @@
# Configuración de Telegram
TELEGRAM_TOKEN=TU_TOKEN_NUEVO_AQUI
# Webhooks de n8n (puedes agregar más aquí en el futuro)
WEBHOOK_CONTRATO=
WEBHOOK_PRINT=
WEBHOOK_VACACIONES=
# --- DATABASE ---
# Usado por el servicio de la base de datos en docker-compose.yml
MYSQL_DATABASE=vanessa_logs
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_ROOT_PASSWORD=rootpassword
# --- SMTP ---
# Usado por el módulo de impresión para enviar correos
SMTP_SERVER=smtp.hostinger.com
SMTP_PORT=465
SMTP_USER=your_email@example.com
SMTP_PASSWORD=your_password
SMTP_RECIPIENT=your_email@example.com

112
Readme.md
View File

@@ -1,6 +1,6 @@
# 🤖 Vanessa Bot Asistente de RH para Vanity
Vanessa es un bot de Telegram escrito en Python que automatiza procesos internos de Recursos Humanos en Vanity. Su objetivo es eliminar fricción operativa: onboarding, solicitudes de RH e impresión de documentos, todo orquestado desde Telegram y conectado a flujos de n8n.
Vanessa es un bot de Telegram escrito en Python que automatiza procesos internos de Recursos Humanos en Vanity. Su objetivo es eliminar fricción operativa: onboarding, solicitudes de RH e impresión de documentos, todo orquestado desde Telegram y conectado a flujos de n8n o servicios de correo.
Este repositorio está pensado como **proyecto Python profesional**, modular y listo para correr 24/7 en producción.
@@ -10,12 +10,12 @@ Este repositorio está pensado como **proyecto Python profesional**, modular y l
Vanessa no es un chatbot genérico: es una interfaz conversacional para procesos reales de negocio.
- Onboarding completo de nuevas socias (/welcome)
- Envío de archivos a impresión (/print)
- Solicitud de vacaciones (/vacaciones)
- Solicitud de permisos por horas (/permiso)
- Onboarding completo de nuevas socias (`/welcome`)
- Envío de archivos a impresión por correo electrónico (`/print`)
- Solicitud de vacaciones (`/vacaciones`)
- Solicitud de permisos por horas (`/permiso`)
Cada flujo es un módulo independiente y todos los datos se envían a **webhooks de n8n** para su procesamiento posterior.
Cada flujo es un módulo independiente, y los datos se envían a **webhooks de n8n** o se procesan directamente, como en el caso de la impresión.
---
@@ -24,7 +24,8 @@ Cada flujo es un módulo independiente y todos los datos se envían a **webhooks
```
vanity_bot/
├── .env # Variables sensibles (tokens, URLs)
├── .env # Variables sensibles (tokens, URLs, credenciales)
├── .env.example # Archivo de ejemplo para variables de entorno
├── main.py # Cerebro principal del bot
├── requirements.txt # Dependencias
├── Dockerfile # Definición del contenedor del bot
@@ -35,7 +36,7 @@ vanity_bot/
├── __init__.py
├── database.py # Módulo de conexión a la base de datos
├── onboarding.py # Flujo /welcome (onboarding RH)
├── printer.py # Flujo /print (impresión)
├── printer.py # Flujo /print (impresión por email)
└── rh_requests.py # /vacaciones y /permiso
```
@@ -43,23 +44,32 @@ vanity_bot/
## 🔐 Configuración (.env)
Crea un archivo `.env` en la raíz del proyecto con el siguiente contenido:
Copia el archivo `.env.example` a `.env` y rellena los valores correspondientes. Este archivo es ignorado por Git para proteger tus credenciales.
```
# --- TELEGRAM ---
TELEGRAM_TOKEN=TU_TOKEN_AQUI
# --- WEBHOOKS N8N ---
WEBHOOK_ONBOARDING=https://flows.soul23.cloud/webhook/contrato
WEBHOOK_PRINT=https://flows.soul23.cloud/webhook/impresion
WEBHOOK_VACACIONES=https://flows.soul23.cloud/webhook/vacaciones
WEBHOOK_ONBOARDING=https://...
WEBHOOK_PRINT=https://...
WEBHOOK_VACACIONES=https://...
# --- DATABASE ---
# Esta URL es para la conexión interna de Docker, no la modifiques si usas Docker Compose.
DATABASE_URL=mysql+mysqlconnector://user:password@db:3306/vanessa_logs
```
# Usado por el servicio de la base de datos en docker-compose.yml
MYSQL_DATABASE=vanessa_logs
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_ROOT_PASSWORD=rootpassword
Nunca subas este archivo al repositorio.
# --- SMTP PARA IMPRESIÓN ---
# Usado por el módulo de impresión para enviar correos
SMTP_SERVER=smtp.hostinger.com
SMTP_PORT=465
SMTP_USER=tu_email@dominio.com
SMTP_PASSWORD=tu_password_de_email
SMTP_RECIPIENT=email_destino@dominio.com
```
---
@@ -76,7 +86,7 @@ Con el archivo `.env` ya configurado, simplemente ejecuta:
```bash
docker-compose up --build
```
Este comando construirá la imagen del bot, descargará la imagen de MySQL, creará los volúmenes y redes, y lanzará ambos servicios. El bot se conectará automáticamente a la base de datos para registrar los logs.
Este comando construirá la imagen del bot, descargará la imagen de MySQL, y lanzará ambos servicios. `docker-compose` leerá las variables del archivo `.env` para configurar los contenedores.
### 3. Detener los servicios
Para detener los contenedores, presiona `Ctrl+C` en la terminal donde se están ejecutando, o ejecuta desde otro terminal:
@@ -86,40 +96,13 @@ docker-compose down
---
## 📦 Instalación Manual
Se recomienda usar un entorno virtual.
```
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
---
## ▶️ Ejecución Manual
```
python main.py
```
Si el token es válido, verás:
```
🧠 Vanessa Brain iniciada y escuchando...
```
**Nota**: Para que la ejecución manual funcione, necesitarás tener una base de datos MySQL corriendo localmente y accesible en la URL especificada en `DATABASE_URL` dentro de tu archivo `.env`.
---
## 🧩 Arquitectura Interna
### main.py (El Cerebro)
- Inicializa el bot de Telegram
- Carga variables de entorno
- Registra los handlers de cada módulo
- Define el menú principal (/start, /help)
- Define el menú principal (`/start`, `/help`)
### modules/database.py
- Gestiona la conexión a la base de datos MySQL con SQLAlchemy.
@@ -127,43 +110,26 @@ Si el token es válido, verás:
- Provee la función `log_request` para registrar interacciones.
### modules/onboarding.py
Flujo conversacional complejo basado en `ConversationHandler`.
- Recolecta información personal, laboral y de emergencia
- Normaliza datos (RFC, CURP, fechas)
- Usa teclados guiados para reducir errores
- Envía un payload estructurado a n8n
Flujo conversacional complejo que recolecta datos de nuevas empleadas y los envía a un webhook de n8n.
### modules/printer.py
- Recibe documentos o imágenes desde Telegram
- Obtiene el enlace temporal de Telegram
- Envía el archivo a una cola de impresión vía webhook
- Recibe documentos o imágenes desde Telegram.
- Descarga el archivo de forma segura desde los servidores de Telegram.
- Se conecta a un servidor SMTP para enviar el archivo como un adjunto por correo electrónico a una dirección predefinida.
### modules/rh_requests.py
- Maneja solicitudes simples de RH: Vacaciones y Permisos por horas.
- Maneja solicitudes simples de RH (Vacaciones y Permisos) y las envía a un webhook de n8n.
---
## 🧠 Filosofía del Proyecto
- Telegram como UI
- Python como cerebro
- n8n como sistema nervioso
- Docker para despliegue
- MySQL para persistencia de logs
- Datos estructurados, no mensajes sueltos
- Modularidad total: cada habilidad se enchufa o se quita
Vanessa no reemplaza RH: elimina fricción humana innecesaria.
---
## 🚀 Extensiones Futuras
- Firma digital de contratos
- Finder de documentos
- Reportes automáticos
- Roles y permisos
- Modo administrador
- **Telegram como UI**: Interfaz conversacional accesible para todos.
- **Python como cerebro**: Lógica de negocio y orquestación.
- **Docker para despliegue**: Entornos consistentes y portátiles.
- **MySQL para persistencia**: Registro auditable de todas las interacciones.
- **SMTP para acciones directas**: Integración con sistemas estándar como el correo.
- **Modularidad total**: Cada habilidad es un componente independiente.
---

View File

@@ -8,7 +8,7 @@ services:
env_file:
- .env
environment:
- DATABASE_URL=mysql+mysqlconnector://user:password@db:3306/vanessa_logs
- DATABASE_URL=mysql+mysqlconnector://${MYSQL_USER}:${MYSQL_PASSWORD}@db:3306/${MYSQL_DATABASE}
depends_on:
- db
volumes:
@@ -19,14 +19,12 @@ services:
container_name: vanessa_db
restart: always
environment:
MYSQL_DATABASE: vanessa_logs
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
volumes:
mysql_data:

View File

@@ -1,9 +1,21 @@
import os
import requests
import smtplib
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
from modules.database import log_request
# --- SMTP Configuration ---
SMTP_SERVER = os.getenv("SMTP_SERVER")
SMTP_PORT = int(os.getenv("SMTP_PORT", 465))
SMTP_USER = os.getenv("SMTP_USER")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
SMTP_RECIPIENT = os.getenv("SMTP_RECIPIENT")
# Estado
ESPERANDO_ARCHIVO = 1
@@ -20,25 +32,48 @@ async def recibir_archivo(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
file_id = archivo.file_id
file_name = getattr(archivo, 'file_name', f"foto_{file_id}.jpg")
# Obtenemos el link de descarga directo de Telegram
file_info = await context.bot.get_file(file_id)
file_url = file_info.file_path
await update.message.reply_text(f"Procesando *{file_name}*... un momento por favor.")
# Enviamos a n8n
webhook = os.getenv("WEBHOOK_PRINT")
payload = {
"user": user.full_name,
"email_user": f"{user.username}@telegram.org", # O pedir el mail antes
"file_url": file_url,
"file_name": file_name,
"tipo": "impresion"
}
try:
requests.post(webhook, json=payload)
await update.message.reply_text(f"✅ Archivo *{file_name}* enviado a cola de impresión.")
except:
await update.message.reply_text("❌ Error al conectar con el servidor de impresión.")
# 1. Descargar el archivo de Telegram
file_info = await context.bot.get_file(file_id)
file_url = file_info.file_path
file_content = requests.get(file_url).content
# 2. Construir el correo
msg = MIMEMultipart()
msg['From'] = SMTP_USER
msg['To'] = SMTP_RECIPIENT
msg['Subject'] = f"Nuevo archivo para imprimir de {user.full_name}"
# Cuerpo del correo
body = f"""
Hola,
El usuario {user.full_name} (Username: @{user.username}, ID: {user.id}) ha enviado un archivo para imprimir.
Nombre del archivo: {file_name}
Este correo ha sido generado automáticamente por Vanessa Bot.
"""
msg.attach(MIMEText(body, 'plain'))
# Adjuntar el archivo
attachment = MIMEApplication(file_content, Name=file_name)
attachment['Content-Disposition'] = f'attachment; filename="{file_name}"'
msg.attach(attachment)
# 3. Enviar el correo
context = ssl.create_default_context()
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context) as server:
server.login(SMTP_USER, SMTP_PASSWORD)
server.sendmail(SMTP_USER, SMTP_RECIPIENT, msg.as_string())
await update.message.reply_text(f"✅ Archivo *{file_name}* enviado a la impresora correctamente.")
except Exception as e:
print(f"Error al enviar correo: {e}") # Log para el admin
await update.message.reply_text("❌ Hubo un error al procesar tu archivo. Por favor, contacta a un administrador.")
return ConversationHandler.END