Bot automatizado en telegram para el matcheo de CV y ofertas laborales.
El proyecto tuvo como objetivo trabajar con el entorno de desarrollo de bots de telegram via BotFather creación de una base de datos, registro de la información entrada, procesamiento de análisis de lenguaje natural (NLP) y coincidencia léxica (Lexical Matching), mediante un enfoque determinista a partir de diccionarios y/o bolsas de palabras.
El flujo de trabajo en un escenario ideal se desarrolla de la siguiente manera:
Ejecuta /start o /Iniciar.
- limpia el estado anterior (contexto.user_data.clear())
- registra al usuario en la base (registrar_usuario(update))
- activa el timer de inactividad (JobQueue, 90s)
- muestra instrucciones y pasa al estado subio_pdf.
El bot recibe el documento y aplica validaciones:
- 2.A Si la sesión está expirada (expired=True) → corta flujo y pide reiniciar.
- 2.B Si el PDF supera el tamaño máximo (2MB) → rechaza y solicita reenviar.
- 2.C Si el usuario superó el límite diario (5 análisis/día) → rechaza y finaliza.
Si pasa validaciones:
- descarga el archivo local (download_to_drive(cv_{id}.pdf))
- guarda registro en DB (envia_pdf(id_telegram, ruta))
- persiste id_cv en memoria de sesión.
Se extrae texto con pdfminer.extract_text().
Se limpia/normaliza con limpia_cv():
- minúsculas
- remoción de acentos (unidecode)
- limpieza por regex de caracteres
- eliminación de stopwords
- deduplicación de tokens
Se ejecutan detectores determinísticos:
- skills: conteo_skills(limpio)
- seniority: ident_seniority(limpio)
- educación: ident_educacion(limpio)
- idiomas: ident_idioma(limpio)
- soft skills: ident_softk(limpio)
- experiencia: detectar_experiencia(texto_cv)
Se guarda en memoria de sesión:
- cv_limpio
- resultados_cv_ (listas encontradas)
- resultados_cv_t_or_f (banderas True/False por categoría)
El bot responde “PDF recibido” y pasa al estado subio_oferta.
El bot recibe texto y aplica validaciones:
- 6.A Si la sesión está expirada → pide reiniciar.
- 6.B Si el bot está esperando menú (esperando_menu=True) → redirige a menú post-análisis.
- 6.C Si la oferta es demasiado corta (len < 100) → rechaza y pide una oferta válida.
Si la oferta es válida:
- reinicia el timeout
- acumula en oferta_buffer (concatena mensajes)
- programa un job diferido (1.5s) para procesar la oferta completa (procesar_analisis_job)
- si el usuario sigue pegando texto, el job previo se cancela y se reprograma (evita analizar “a mitad”).
Cuando pasan ~1.5s sin nuevos fragmentos:
- se toma oferta_buffer
- 7.A Si el límite diario ya no permite consultas → se corta con mensaje “límite alcanzado”.
- 7.B Si no hay oferta en buffer → no hace nada.
Se limpia oferta con limpia_cv(oferta) (mismo pipeline que CV).
Se detectan señales con los mismos extractores:
- skills
- seniority
- educación
- idiomas
- soft skills
- experiencia
Se guarda en memoria:
- resultados_oferta_
- resultados_oferta_t_f
Entra el “motor lógico” (no IA):
-
9.A Si faltan categorías en la oferta (False en resultados_oferta_t_f)
→ balanceo_pesos() redistribuye los pesos solo entre categorías presentes. -
9.B Si están todas presentes
→ usa los pesos estándar (pesos_todas_categorias).
Se ejecuta matcheo(oferta, cv):
- para cada categoría, intersecta listas (set intersection)
- guarda coincidencias, cantidad y total_oferta.
Se ejecuta calculo_match():
- ratio = coincidencias / total_oferta
- score_categoria = ratio * peso_balanceado
- suma de categorías → score final.
Se ejecuta score_final():
-
12.A Si no hay coincidencias relevantes → devuelve mensaje fallback (“no encontró coincidencias”).
-
12.B Si hay score → selecciona texto según rangos:
-
≥ 80: ideal
-
≥ 60: muy bueno
-
≥ 40: parcial
-
< 40: bajo
devuelve: Score de Matcheo: X + análisis narrativo.
Se guarda todo en SQLite:
- oferta (envia_oferta)
- análisis final (carga_analisis)
- actualización del límite diario (puede_consultar())
El bot envía:
- información identificada en CV
- información identificada en la oferta
- score final + texto
- Analizar otro CV
- Pegar otra oferta (mismo CV)
- Salir
- 15.A Opción 1 → borra sesión, vuelve a estado subio_pdf.
- 15.B Opción 2 → mantiene CV, limpia buffer oferta, vuelve a subio_oferta.
- 15.C Opción 3 → cierra sesión, cancela timeout, termina conversación.
- 15.D Si el usuario responde cualquier otra cosa → muestra ayuda “solo 1,2,3”.
Archivo: bot.py (tu archivo principal)
- Gestionar conversación en Telegram.
- Manejar estados del flujo.
- Controlar sesión del usuario.
- Activar timeouts.
- Coordinar todo el pipeline.
- ConversationHandler.
- Estados: subio_pdf, subio_oferta, menu.
- Handlers principales: saludo(), pdf_recibido(), oferta_subida(), menu_post(), invalid_message().
- Control de sesión: start_timeout(), cancel_timeout(), timeout_job().
Nota: Este módulo es el controlador central del sistema.
Archivo: logic.py (parte de funciones)
- Extraer texto del PDF.
- Limpiar texto.
- Tokenizar.
- Normalizar datos.
- Preparar insumo para análisis.
- texto_cv() → extrae texto con pdfminer.
- limpia_cv() → limpieza fuerte del texto (lowercase, regex, unidecode, stopwords, deduplicación).
Nota: Este módulo convierte CV/oferta en datos analizables.
Archivo: logic.py
- Detectar información estructurada dentro del texto.
- conteo_skills()
- ident_seniority()
- ident_educacion()
- ident_idioma()
- ident_softk()
- detectar_experiencia()
Nota: Este módulo transforma texto en features ATS.
Archivo: logic.py
- Calcular el match real entre CV y oferta.
- Balanceador de pesos: balanceo_pesos() redistribuye ponderaciones si faltan categorías.
- Motor de coincidencias: matcheo() realiza la intersección por categoría.
- Cálculo ponderado: calculo_match() evalúa el ratio coincidencias * peso.
- Generador narrativo final: score_final() hace la selección de plantilla textual.
Nota: Este módulo es el núcleo lógico del sistema.
Archivo: db_manager.py
- Guardar usuarios.
- Guardar CVs.
- Guardar ofertas.
- Guardar análisis.
- Controlar límites diarios.
- crea_bd()
- registrar_usuario()
- envia_pdf()
- envia_oferta()
- carga_analisis()
- puede_consultar()
Nota: Este módulo es la capa de datos.
Archivo: bot.py
- Resolver problema de ofertas largas enviadas en varios mensajes.
- oferta_buffer
- job_queue.run_once()
- procesar_analisis_job()
Nota: Este módulo actúa como sistema de ensamblado de entrada.
Este proyecto se encuentra desplegado en un servidor cloud Linux con Ubuntu 22.04 LTS.
El despliegue incluye:
- Infraestructura en servidor cloud (Open Cloud VPS)
- Sistema operativo Ubuntu 22.04 LTS
- Entorno de ejecución Python
- Base de datos SQLite local
- Integración con Telegram Bot API
- Ejecución continua mediante polling para disponibilidad 24/7

