""" Utilidades para el Sales Bot """ import re import os import logging logger = logging.getLogger(__name__) def validar_token_outgoing(token): """ Valida el token de webhooks salientes de Mattermost """ if not token: return False expected_tokens = [ os.getenv('MATTERMOST_WEBHOOK_SECRET'), os.getenv('MATTERMOST_OUTGOING_TOKEN'), ] return token in [t for t in expected_tokens if t] def extraer_monto(texto): """ Extrae el monto de una venta del texto del mensaje. Soporta formatos: - @monto 1500 - $1,500.00 - $1500 - 1500 pesos """ if not texto: return None texto = texto.lower() # Buscar formato @monto XXXX patron_monto = r'@monto\s+\$?([\d,]+\.?\d*)' match = re.search(patron_monto, texto) if match: monto_str = match.group(1).replace(',', '') try: return float(monto_str) except ValueError: pass # Buscar formato $X,XXX.XX o $XXXX patron_dinero = r'\$\s*([\d,]+\.?\d*)' match = re.search(patron_dinero, texto) if match: monto_str = match.group(1).replace(',', '') try: return float(monto_str) except ValueError: pass # Buscar formato XXXX pesos patron_pesos = r'([\d,]+\.?\d*)\s*pesos' match = re.search(patron_pesos, texto) if match: monto_str = match.group(1).replace(',', '') try: return float(monto_str) except ValueError: pass return None def extraer_cliente(texto): """ Extrae el nombre del cliente del texto del mensaje. Soporta formatos: - @cliente Juan Pérez - cliente: Juan Pérez - a Juan Pérez """ if not texto: return None # Buscar formato @cliente NOMBRE patron_cliente = r'@cliente\s+([^\n@$]+)' match = re.search(patron_cliente, texto, re.IGNORECASE) if match: cliente = match.group(1).strip() # Limpiar palabras clave que no son parte del nombre cliente = re.sub(r'\s*@monto.*$', '', cliente, flags=re.IGNORECASE) return cliente.strip() if cliente.strip() else None # Buscar formato "cliente: NOMBRE" o "cliente NOMBRE" patron_cliente2 = r'cliente[:\s]+([^\n@$]+)' match = re.search(patron_cliente2, texto, re.IGNORECASE) if match: cliente = match.group(1).strip() cliente = re.sub(r'\s*@monto.*$', '', cliente, flags=re.IGNORECASE) return cliente.strip() if cliente.strip() else None # Buscar formato "a NOMBRE" (después de un monto) patron_a = r'\$[\d,\.]+\s+a\s+([^\n@$]+)' match = re.search(patron_a, texto, re.IGNORECASE) if match: cliente = match.group(1).strip() return cliente.strip() if cliente.strip() else None return None def formatear_moneda(valor, simbolo='$', decimales=2): """ Formatea un número como moneda. Ejemplo: 1500.5 -> $1,500.50 """ if valor is None: return f"{simbolo}0.00" try: valor = float(valor) return f"{simbolo}{valor:,.{decimales}f}" except (ValueError, TypeError): return f"{simbolo}0.00" def extraer_tubos(texto): """ Extrae la cantidad de tubos del texto del mensaje. Soporta formatos: - @tubos 5 - tubos: 5 - 5 tubos """ if not texto: return None texto = texto.lower() # Buscar formato @tubos XXXX patron_tubos = r'@tubos\s+(\d+)' match = re.search(patron_tubos, texto) if match: try: return int(match.group(1)) except ValueError: pass # Buscar formato "tubos: X" o "tubos X" patron_tubos2 = r'tubos[:\s]+(\d+)' match = re.search(patron_tubos2, texto) if match: try: return int(match.group(1)) except ValueError: pass # Buscar formato "X tubos" patron_tubos3 = r'(\d+)\s*tubos?' match = re.search(patron_tubos3, texto) if match: try: return int(match.group(1)) except ValueError: pass return None # ==================== NUEVAS FUNCIONES FASE 1 ==================== def extraer_id_venta(texto): """ Extrae el ID de venta del texto del comando. Soporta formatos: - /cancelar 123 - /editar 123 @monto 1500 - #123 """ if not texto: return None # Buscar número al inicio o después del comando patron_id = r'(?:^|\s)#?(\d+)' match = re.search(patron_id, texto.strip()) if match: try: return int(match.group(1)) except ValueError: pass return None def extraer_motivo(texto): """ Extrae el motivo de cancelación del texto. Soporta formatos: - /cancelar 123 "cliente no pagó" - /cancelar 123 motivo: cliente canceló """ if not texto: return None # Buscar texto entre comillas patron_comillas = r'["\']([^"\']+)["\']' match = re.search(patron_comillas, texto) if match: return match.group(1).strip() # Buscar después de "motivo:" patron_motivo = r'motivo[:\s]+(.+)$' match = re.search(patron_motivo, texto, re.IGNORECASE) if match: return match.group(1).strip() # Si hay texto después del ID, usarlo como motivo patron_resto = r'^\d+\s+(.+)$' match = re.search(patron_resto, texto.strip()) if match: motivo = match.group(1).strip() # Excluir si es otro comando if not motivo.startswith('@') and not motivo.startswith('/'): return motivo return None def extraer_mes(texto): """ Extrae el mes del texto del comando. Soporta formatos: - 2026-01 - 01-2026 - enero 2026 - enero """ if not texto: return None texto = texto.strip().lower() # Formato YYYY-MM patron_iso = r'(\d{4})-(\d{1,2})' match = re.search(patron_iso, texto) if match: return f"{match.group(1)}-{match.group(2).zfill(2)}" # Formato MM-YYYY patron_inv = r'(\d{1,2})-(\d{4})' match = re.search(patron_inv, texto) if match: return f"{match.group(2)}-{match.group(1).zfill(2)}" # Nombres de meses en español meses = { 'enero': '01', 'febrero': '02', 'marzo': '03', 'abril': '04', 'mayo': '05', 'junio': '06', 'julio': '07', 'agosto': '08', 'septiembre': '09', 'octubre': '10', 'noviembre': '11', 'diciembre': '12' } for nombre, num in meses.items(): if nombre in texto: # Buscar año patron_anio = r'(\d{4})' match = re.search(patron_anio, texto) if match: return f"{match.group(1)}-{num}" # Si no hay año, usar el actual from datetime import datetime return f"{datetime.now().year}-{num}" return None def parsear_formato_exportar(texto): """ Parsea los parámetros del comando /exportar. Retorna (formato, mes) """ if not texto: return 'excel', None texto = texto.strip().lower() # Detectar formato formato = 'excel' if 'csv' in texto: formato = 'csv' texto = texto.replace('csv', '').strip() # Detectar mes mes = extraer_mes(texto) return formato, mes def validar_tokens_comando(token, nombre_comando): """ Valida tokens para comandos slash. """ if not token: return False expected_tokens = [ os.getenv(f'MATTERMOST_SLASH_TOKEN_{nombre_comando.upper()}'), os.getenv('MATTERMOST_OUTGOING_TOKEN'), os.getenv('MATTERMOST_WEBHOOK_SECRET'), ] return token in [t for t in expected_tokens if t]