"""Que preguntar cuando falta un dato para ejecutar una accion. Pulsar una accion en el panel no puede limitarse a recitar la frase que la dispara —"diga «busca el fichero» y a continuacion lo que busca"—: eso es un manual de instrucciones, y uno pulsa justo porque no quiere leerse el manual. Lo natural es que pregunte lo que falta y lo haga. busca el fichero -> ¿Que fichero, señor? manda la ventana a la pantalla -> ¿A que pantalla, señor? mata el proceso -> ¿Que proceso, señor? ## Como sale la pregunta Del propio catalogo, no de una lista escrita a mano ni de un modelo. Las frases del catalogo son regulares —verbo + articulo + objeto— asi que basta con buscar el ULTIMO articulo y quedarse con lo que va detras. Y se pregunta por el OBJETO, no conjugando el verbo. "¿Que fichero, señor?" es tan natural como "¿Que fichero busco, señor?" y no obliga a conjugar: pasar de "busca" a "busco", de "leeme" a "leo" o de "pon" a "pongo" pide un diccionario de verbos irregulares para ganar nada. Si la preposicion viene delante del articulo, se conserva: sin ella, "manda la ventana a la pantalla" preguntaria "¿Que pantalla?" y se pierde el "a", que es justo lo que dice que la respuesta es un destino. """ import re import unicodedata ARTICULOS = {"el", "la", "los", "las", "un", "una", "unos", "unas"} # Las contracciones ya llevan la preposicion dentro. CONTRACCIONES = {"al": "a", "del": "de"} PREPOSICIONES = {"a", "de", "en", "con", "por", "para", "sobre", "hacia", "desde"} # Cuando no hay de donde sacarlo. Vale para "esta corriendo" o "tengo instalado", # donde el objeto no aparece en la frase. GENERICA = "¿El que, señor?" def _sin_tildes(t: str) -> str: t = unicodedata.normalize("NFD", t) return "".join(c for c in t if unicodedata.category(c) != "Mn") def pregunta_por(accion) -> str: """La pregunta que rellena el hueco de una accion que espera argumento. La clave: el argumento va SIEMPRE al final de la frase, porque asi lo recoge el emparejador. Asi que la pregunta se decide mirando como TERMINA. """ frases = getattr(accion, "frases", None) or [] if not frases: return GENERICA palabras = _sin_tildes(frases[0].lower()).split() if not palabras: return GENERICA ultima = palabras[-1] # 1. Termina en preposicion: el hueco va justo detras. # "busca el precio de" -> ¿El precio de que, señor? # "busca imagenes de" -> ¿Imagenes de que, señor? if ultima in PREPOSICIONES: trozo = [] # Sin la primera palabra: es el verbo, y en la pregunta sobra. # "busca imagenes de" tiene que dar "¿Imagenes de que?", no # "¿Busca imagenes de que?". for p in reversed(palabras[1:-1]): if p in ARTICULOS or p in CONTRACCIONES: trozo.insert(0, p) break if p in PREPOSICIONES: break trozo.insert(0, p) if len(trozo) >= 2: break if trozo: frase = " ".join(trozo).capitalize() return f"¿{frase} {ultima} que, señor?" return GENERICA # 2. "busca en ": lo que falta es QUE buscar, no el sitio. # Sin esta regla, "busca en la wikipedia" preguntaba "¿En que # wikipedia?", que no tiene sentido: solo hay una. if "en" in palabras: i = palabras.index("en") sitio = " ".join(palabras[i:]) verbo = palabras[0] if verbo.startswith("busca"): return f"¿Que busco {sitio}, señor?" return f"¿Que {sitio}, señor?" # 3. Lo normal: articulo + objeto al final. # "busca el fichero" -> ¿Que fichero, señor? # "manda la ventana a la pantalla" -> ¿A que pantalla, señor? for i in range(len(palabras) - 1, -1, -1): p = palabras[i] objeto = palabras[i + 1] if i + 1 < len(palabras) else "" if not objeto: continue if p in CONTRACCIONES: return f"¿{CONTRACCIONES[p].capitalize()} que {objeto}, señor?" if p in ARTICULOS: anterior = palabras[i - 1] if i > 0 else "" if anterior in PREPOSICIONES: return f"¿{anterior.capitalize()} que {objeto}, señor?" return f"¿Que {objeto}, señor?" return GENERICA def confirmacion(accion) -> str: """Lo que se pregunta antes de algo que no tiene vuelta atras. La orden se CITA en vez de meterla en la frase. Las del catalogo estan en imperativo —"cierra el navegador"— y encajarlas da cosas como "Voy a cierra el navegador". Conjugarlas al infinitivo pediria un diccionario de verbos irregulares (cierra->cerrar, pon->poner, ve->ir) para ganar poco: entre comillas se lee bien y ademas queda claro que es literalmente la orden. """ frase = (getattr(accion, "frases", None) or [""])[0] return f"«{frase}». Esto no tiene vuelta atras. ¿Confirma, señor?" # Lo que cuenta como si y como no. Se comparan sin tildes y sin puntuacion, # porque llegan de Whisper: "sí", "Si.", "SÍ" y "sip" son lo mismo. SI = {"si", "sip", "claro", "vale", "venga", "adelante", "confirmo", "hazlo", "por supuesto", "correcto", "afirmativo", "eso es", "dale", "ok", "okey"} NO = {"no", "nop", "nada", "dejalo", "olvidalo", "cancela", "cancelalo", "para", "mejor no", "negativo", "ni hablar", "espera"} def es_si(texto: str): """True si es un si, False si es un no, None si no es ni una cosa ni otra.""" limpio = _sin_tildes(re.sub(r"[¡!¿?.,;]", "", (texto or "").lower())).strip() if not limpio: return None if limpio in SI: return True if limpio in NO: return False # Tambien al principio: "si, hazlo" o "no, dejalo" primera = limpio.split()[0] if primera in SI: return True if primera in NO: return False return None