diff --git a/.env b/.env index cc96196..2bd42b6 100644 --- a/.env +++ b/.env @@ -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) \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0a96573 --- /dev/null +++ b/.env.example @@ -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 diff --git a/Readme.md b/Readme.md index a94962a..08001e9 100644 --- a/Readme.md +++ b/Readme.md @@ -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. --- diff --git a/docker-compose.yml b/docker-compose.yml index 7ef41fa..b71e889 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/modules/printer.py b/modules/printer.py index ed1c886..5ca13f3 100644 --- a/modules/printer.py +++ b/modules/printer.py @@ -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