mirror of
https://github.com/marcogll/telegram_new_socias.git
synced 2026-01-13 13:15:16 +00:00
Merge pull request #1 from marcogll/feat/docker-mysql-logging-13516104967243096474
feat: Dockerize application and add MySQL logging
This commit is contained in:
6
.env
6
.env
@@ -3,4 +3,8 @@ TELEGRAM_TOKEN=TU_TOKEN_NUEVO_AQUI
|
|||||||
|
|
||||||
# Webhooks de n8n (puedes agregar más aquí en el futuro)
|
# Webhooks de n8n (puedes agregar más aquí en el futuro)
|
||||||
WEBHOOK_CONTRATO=https://flows.soul23.cloud/webhook/DuXh9Oi7SCAMf9
|
WEBHOOK_CONTRATO=https://flows.soul23.cloud/webhook/DuXh9Oi7SCAMf9
|
||||||
# WEBHOOK_VACACIONES=https://... (futuro)
|
WEBHOOK_PRINT=
|
||||||
|
WEBHOOK_VACACIONES=
|
||||||
|
|
||||||
|
# --- DATABASE ---
|
||||||
|
DATABASE_URL=mysql+mysqlconnector://user:password@db:3306/vanessa_logs
|
||||||
|
|||||||
93
.gitignore
vendored
93
.gitignore
vendored
@@ -1,61 +1,46 @@
|
|||||||
# =========================
|
# Environments
|
||||||
# Secrets & Environment
|
|
||||||
# =========================
|
|
||||||
.env
|
.env
|
||||||
.env.*
|
|
||||||
*.env
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Python
|
|
||||||
# =========================
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
|
|
||||||
# Virtual environments
|
|
||||||
venv/
|
venv/
|
||||||
.venv/
|
|
||||||
env/
|
|
||||||
ENV/
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Logs & Runtime
|
|
||||||
# =========================
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
*.sqlite
|
|
||||||
*.db
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# OS & Editors
|
|
||||||
# =========================
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# VS Code
|
|
||||||
.vscode/
|
|
||||||
*.code-workspace
|
|
||||||
|
|
||||||
# JetBrains
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# =========================
|
# Byte-compiled / optimized / C extensions
|
||||||
# Build / Distribution
|
__pycache__/
|
||||||
# =========================
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
build/
|
build/
|
||||||
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
*.egg-info/
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Systemd / Deploy Artifacts
|
|
||||||
# =========================
|
|
||||||
*.service
|
|
||||||
*.pid
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Temp / Downloads
|
|
||||||
# =========================
|
|
||||||
tmp/
|
|
||||||
temp/
|
|
||||||
downloads/
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*,cover
|
||||||
|
*.log
|
||||||
|
.pytest_cache/
|
||||||
|
.hypothesis/
|
||||||
|
|||||||
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Usar una imagen base de Python
|
||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
# Establecer el directorio de trabajo
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copiar los archivos de requisitos e instalar dependencias
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copiar el resto del código de la aplicación
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Comando para ejecutar la aplicación
|
||||||
|
CMD ["python", "main.py"]
|
||||||
91
Readme.md
91
Readme.md
@@ -27,10 +27,13 @@ vanity_bot/
|
|||||||
├── .env # Variables sensibles (tokens, URLs)
|
├── .env # Variables sensibles (tokens, URLs)
|
||||||
├── main.py # Cerebro principal del bot
|
├── main.py # Cerebro principal del bot
|
||||||
├── requirements.txt # Dependencias
|
├── requirements.txt # Dependencias
|
||||||
|
├── Dockerfile # Definición del contenedor del bot
|
||||||
|
├── docker-compose.yml # Orquestación de servicios (bot + db)
|
||||||
├── README.md # Este documento
|
├── README.md # Este documento
|
||||||
│
|
│
|
||||||
└── modules/ # Habilidades del bot
|
└── modules/ # Habilidades del bot
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
|
├── database.py # Módulo de conexión a la base de datos
|
||||||
├── onboarding.py # Flujo /welcome (onboarding RH)
|
├── onboarding.py # Flujo /welcome (onboarding RH)
|
||||||
├── printer.py # Flujo /print (impresión)
|
├── printer.py # Flujo /print (impresión)
|
||||||
└── rh_requests.py # /vacaciones y /permiso
|
└── rh_requests.py # /vacaciones y /permiso
|
||||||
@@ -50,13 +53,40 @@ TELEGRAM_TOKEN=TU_TOKEN_AQUI
|
|||||||
WEBHOOK_ONBOARDING=https://flows.soul23.cloud/webhook/contrato
|
WEBHOOK_ONBOARDING=https://flows.soul23.cloud/webhook/contrato
|
||||||
WEBHOOK_PRINT=https://flows.soul23.cloud/webhook/impresion
|
WEBHOOK_PRINT=https://flows.soul23.cloud/webhook/impresion
|
||||||
WEBHOOK_VACACIONES=https://flows.soul23.cloud/webhook/vacaciones
|
WEBHOOK_VACACIONES=https://flows.soul23.cloud/webhook/vacaciones
|
||||||
|
|
||||||
|
# --- 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
|
||||||
```
|
```
|
||||||
|
|
||||||
Nunca subas este archivo al repositorio.
|
Nunca subas este archivo al repositorio.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Instalación
|
## 🐳 Ejecución con Docker (Recomendado)
|
||||||
|
|
||||||
|
El proyecto está dockerizado para facilitar su despliegue.
|
||||||
|
|
||||||
|
### 1. Pre-requisitos
|
||||||
|
- Docker
|
||||||
|
- Docker Compose
|
||||||
|
|
||||||
|
### 2. Levantar los servicios
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 3. Detener los servicios
|
||||||
|
Para detener los contenedores, presiona `Ctrl+C` en la terminal donde se están ejecutando, o ejecuta desde otro terminal:
|
||||||
|
```bash
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Instalación Manual
|
||||||
|
|
||||||
Se recomienda usar un entorno virtual.
|
Se recomienda usar un entorno virtual.
|
||||||
|
|
||||||
@@ -79,82 +109,37 @@ Si el token es válido, verás:
|
|||||||
```
|
```
|
||||||
🧠 Vanessa Brain iniciada y escuchando...
|
🧠 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
|
## 🧩 Arquitectura Interna
|
||||||
|
|
||||||
### main.py (El Cerebro)
|
### main.py (El Cerebro)
|
||||||
|
|
||||||
- Inicializa el bot de Telegram
|
- Inicializa el bot de Telegram
|
||||||
- Carga variables de entorno
|
- Carga variables de entorno
|
||||||
- Registra los handlers de cada módulo
|
- Registra los handlers de cada módulo
|
||||||
- Define el menú principal (/start, /help)
|
- Define el menú principal (/start, /help)
|
||||||
|
|
||||||
Nada de lógica de negocio vive aquí. Solo coordinación.
|
### modules/database.py
|
||||||
|
- Gestiona la conexión a la base de datos MySQL con SQLAlchemy.
|
||||||
---
|
- Define el modelo `RequestLog` para la tabla de logs.
|
||||||
|
- Provee la función `log_request` para registrar interacciones.
|
||||||
|
|
||||||
### modules/onboarding.py
|
### modules/onboarding.py
|
||||||
|
|
||||||
Flujo conversacional complejo basado en `ConversationHandler`.
|
Flujo conversacional complejo basado en `ConversationHandler`.
|
||||||
|
|
||||||
- Recolecta información personal, laboral y de emergencia
|
- Recolecta información personal, laboral y de emergencia
|
||||||
- Normaliza datos (RFC, CURP, fechas)
|
- Normaliza datos (RFC, CURP, fechas)
|
||||||
- Usa teclados guiados para reducir errores
|
- Usa teclados guiados para reducir errores
|
||||||
- Envía un payload estructurado a n8n
|
- Envía un payload estructurado a n8n
|
||||||
|
|
||||||
El diseño es **estado → pregunta → respuesta → siguiente estado**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### modules/printer.py
|
### modules/printer.py
|
||||||
|
|
||||||
- Recibe documentos o imágenes desde Telegram
|
- Recibe documentos o imágenes desde Telegram
|
||||||
- Obtiene el enlace temporal de Telegram
|
- Obtiene el enlace temporal de Telegram
|
||||||
- Envía el archivo a una cola de impresión vía webhook
|
- Envía el archivo a una cola de impresión vía webhook
|
||||||
|
|
||||||
Telegram se usa como interfaz, n8n como backend operativo.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### modules/rh_requests.py
|
### modules/rh_requests.py
|
||||||
|
- Maneja solicitudes simples de RH: Vacaciones y Permisos por horas.
|
||||||
- Maneja solicitudes simples de RH
|
|
||||||
- Vacaciones
|
|
||||||
- Permisos por horas
|
|
||||||
|
|
||||||
El bot solo valida y recopila; la lógica de aprobación vive fuera.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚙️ Ejecución Automática con systemd (Linux)
|
|
||||||
|
|
||||||
Ejemplo de servicio:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=Vanessa Bot
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
User=vanity
|
|
||||||
WorkingDirectory=/opt/vanity_bot
|
|
||||||
EnvironmentFile=/opt/vanity_bot/.env
|
|
||||||
ExecStart=/opt/vanity_bot/venv/bin/python main.py
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Luego:
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable vanessa
|
|
||||||
sudo systemctl start vanessa
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -163,6 +148,8 @@ sudo systemctl start vanessa
|
|||||||
- Telegram como UI
|
- Telegram como UI
|
||||||
- Python como cerebro
|
- Python como cerebro
|
||||||
- n8n como sistema nervioso
|
- n8n como sistema nervioso
|
||||||
|
- Docker para despliegue
|
||||||
|
- MySQL para persistencia de logs
|
||||||
- Datos estructurados, no mensajes sueltos
|
- Datos estructurados, no mensajes sueltos
|
||||||
- Modularidad total: cada habilidad se enchufa o se quita
|
- Modularidad total: cada habilidad se enchufa o se quita
|
||||||
|
|
||||||
|
|||||||
32
docker-compose.yml
Normal file
32
docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
bot:
|
||||||
|
build: .
|
||||||
|
container_name: vanessa_bot
|
||||||
|
restart: always
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=mysql+mysqlconnector://user:password@db:3306/vanessa_logs
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: vanessa_db
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MYSQL_DATABASE: vanessa_logs
|
||||||
|
MYSQL_USER: user
|
||||||
|
MYSQL_PASSWORD: password
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
3
main.py
3
main.py
@@ -9,6 +9,7 @@ from telegram.ext import Application, Defaults, CommandHandler, ContextTypes
|
|||||||
from modules.onboarding import onboarding_handler
|
from modules.onboarding import onboarding_handler
|
||||||
from modules.printer import print_handler
|
from modules.printer import print_handler
|
||||||
from modules.rh_requests import vacaciones_handler, permiso_handler
|
from modules.rh_requests import vacaciones_handler, permiso_handler
|
||||||
|
from modules.database import log_request
|
||||||
# from modules.finder import finder_handler (Si lo creas después)
|
# from modules.finder import finder_handler (Si lo creas después)
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@@ -18,6 +19,8 @@ logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
|||||||
|
|
||||||
async def menu_principal(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def menu_principal(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""Muestra el menú de opciones de Vanessa"""
|
"""Muestra el menú de opciones de Vanessa"""
|
||||||
|
user = update.effective_user
|
||||||
|
log_request(user.id, user.username, "start", update.message.text)
|
||||||
texto = (
|
texto = (
|
||||||
"👩💼 **Hola, soy Vanessa. ¿En qué puedo ayudarte hoy?**\n\n"
|
"👩💼 **Hola, soy Vanessa. ¿En qué puedo ayudarte hoy?**\n\n"
|
||||||
"📝 `/welcome` - Iniciar onboarding/contrato\n"
|
"📝 `/welcome` - Iniciar onboarding/contrato\n"
|
||||||
|
|||||||
64
modules/database.py
Normal file
64
modules/database.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import os
|
||||||
|
from sqlalchemy import create_engine, Column, Integer, String, DateTime, MetaData, Table
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Configuración de logging
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
# Obtener la URL de la base de datos desde las variables de entorno
|
||||||
|
DATABASE_URL = os.getenv("DATABASE_URL", "mysql+mysqlconnector://user:password@db:3306/vanessa_logs")
|
||||||
|
|
||||||
|
# Crear el motor de la base de datos
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
metadata = MetaData()
|
||||||
|
|
||||||
|
# Base para los modelos declarativos
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
# Clase que mapea a la tabla de logs
|
||||||
|
class RequestLog(Base):
|
||||||
|
__tablename__ = 'request_logs'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
telegram_id = Column(String(50))
|
||||||
|
username = Column(String(100))
|
||||||
|
command = Column(String(100))
|
||||||
|
message = Column(String(500))
|
||||||
|
created_at = Column(DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
|
# Función para inicializar la base de datos
|
||||||
|
def init_db():
|
||||||
|
try:
|
||||||
|
logging.info("Inicializando la base de datos y creando tablas si no existen...")
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
logging.info("Tablas verificadas/creadas correctamente.")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error al inicializar la base de datos: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Crear una sesión para interactuar con la base de datos
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
# Función para registrar una solicitud en la base de datos
|
||||||
|
def log_request(telegram_id, username, command, message):
|
||||||
|
db_session = SessionLocal()
|
||||||
|
try:
|
||||||
|
log_entry = RequestLog(
|
||||||
|
telegram_id=str(telegram_id),
|
||||||
|
username=username,
|
||||||
|
command=command,
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
db_session.add(log_entry)
|
||||||
|
db_session.commit()
|
||||||
|
logging.info(f"Log guardado: {command} de {username}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error al guardar el log: {e}")
|
||||||
|
db_session.rollback()
|
||||||
|
finally:
|
||||||
|
db_session.close()
|
||||||
|
|
||||||
|
# Inicializar la base de datos al arrancar el módulo
|
||||||
|
init_db()
|
||||||
@@ -17,6 +17,8 @@ from telegram.ext import (
|
|||||||
Defaults,
|
Defaults,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from modules.database import log_request
|
||||||
|
|
||||||
# --- 1. CARGA DE ENTORNO ---
|
# --- 1. CARGA DE ENTORNO ---
|
||||||
load_dotenv() # Carga las variables del archivo .env
|
load_dotenv() # Carga las variables del archivo .env
|
||||||
TOKEN = os.getenv("TELEGRAM_TOKEN")
|
TOKEN = os.getenv("TELEGRAM_TOKEN")
|
||||||
@@ -115,6 +117,7 @@ TECLADO_RELACION_EMERGENCIA = ReplyKeyboardMarkup(
|
|||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
user = update.effective_user
|
user = update.effective_user
|
||||||
context.user_data.clear()
|
context.user_data.clear()
|
||||||
|
log_request(user.id, user.username, "welcome", update.message.text)
|
||||||
|
|
||||||
context.user_data["metadata"] = {
|
context.user_data["metadata"] = {
|
||||||
"telegram_id": user.id,
|
"telegram_id": user.id,
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import os
|
|||||||
import requests
|
import requests
|
||||||
from telegram import Update
|
from telegram import Update
|
||||||
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
|
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
|
||||||
|
from modules.database import log_request
|
||||||
|
|
||||||
# Estado
|
# Estado
|
||||||
ESPERANDO_ARCHIVO = 1
|
ESPERANDO_ARCHIVO = 1
|
||||||
|
|
||||||
async def start_print(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def start_print(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
|
user = update.effective_user
|
||||||
|
log_request(user.id, user.username, "print", update.message.text)
|
||||||
await update.message.reply_text("🖨️ **Servicio de Impresión**\n\nPor favor, envíame el archivo (PDF, DOCX o Imagen) que deseas imprimir/enviar.")
|
await update.message.reply_text("🖨️ **Servicio de Impresión**\n\nPor favor, envíame el archivo (PDF, DOCX o Imagen) que deseas imprimir/enviar.")
|
||||||
return ESPERANDO_ARCHIVO
|
return ESPERANDO_ARCHIVO
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,20 @@ import os
|
|||||||
import requests
|
import requests
|
||||||
from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||||
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
|
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
|
||||||
|
from modules.database import log_request
|
||||||
|
|
||||||
TIPO_SOLICITUD, FECHAS, MOTIVO = range(3)
|
TIPO_SOLICITUD, FECHAS, MOTIVO = range(3)
|
||||||
|
|
||||||
async def start_vacaciones(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def start_vacaciones(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
|
user = update.effective_user
|
||||||
|
log_request(user.id, user.username, "vacaciones", update.message.text)
|
||||||
context.user_data['tipo'] = 'Vacaciones'
|
context.user_data['tipo'] = 'Vacaciones'
|
||||||
await update.message.reply_text("🌴 **Solicitud de Vacaciones**\n\n¿Para qué fechas las necesitas? (Ej: 10 al 15 de Octubre)")
|
await update.message.reply_text("🌴 **Solicitud de Vacaciones**\n\n¿Para qué fechas las necesitas? (Ej: 10 al 15 de Octubre)")
|
||||||
return FECHAS
|
return FECHAS
|
||||||
|
|
||||||
async def start_permiso(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
async def start_permiso(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
|
||||||
|
user = update.effective_user
|
||||||
|
log_request(user.id, user.username, "permiso", update.message.text)
|
||||||
context.user_data['tipo'] = 'Permiso Especial'
|
context.user_data['tipo'] = 'Permiso Especial'
|
||||||
await update.message.reply_text("⏱️ **Solicitud de Permiso**\n\n¿Para qué día y horario lo necesitas?")
|
await update.message.reply_text("⏱️ **Solicitud de Permiso**\n\n¿Para qué día y horario lo necesitas?")
|
||||||
return FECHAS
|
return FECHAS
|
||||||
|
|||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
python-telegram-bot
|
||||||
|
python-dotenv
|
||||||
|
requests
|
||||||
|
SQLAlchemy
|
||||||
|
mysql-connector-python
|
||||||
Reference in New Issue
Block a user