--- name: imap_connect kind: function lang: py domain: infra version: "1.0.0" purity: impure signature: "def imap_connect(host: str, port: int = 993, user: str = '', password: str = '', mailbox: str = 'INBOX', use_ssl: bool = True, timeout_s: float = 30.0) -> dict" description: "Abre y autentica una conexion IMAP (IMAP4_SSL por defecto, IMAP4 en claro si use_ssl=False) con usuario + app-password (NO OAuth), hace login y select(mailbox), y devuelve el objeto imaplib vivo dentro del dict de estado para componer el resto de operaciones del grupo email/imap. Defaults Gmail: host imap.gmail.com, port 993. Devuelve {status:'ok', conn, mailbox, num_messages} o {status:'error', error}. Nunca lanza. Las credenciales las pasa la capa app (via pass/vault), no se resuelven aqui." tags: [email, imap, infra, mailbox, network] params: - name: host desc: "Servidor IMAP, ej. imap.gmail.com. Vacio devuelve status error." - name: port desc: "Puerto IMAP. Default 993 (IMAPS sobre SSL). 143 para STARTTLS/plano." - name: user desc: "Direccion de correo / usuario de la cuenta." - name: password desc: "App-password (16 chars en Gmail, requiere 2FA) o contrasena del proveedor. NO OAuth." - name: mailbox desc: "Buzon a seleccionar tras autenticar. Default 'INBOX'." - name: use_ssl desc: "True (default) usa IMAP4_SSL cifrado. False usa IMAP4 en claro (solo redes de confianza/test)." - name: timeout_s desc: "Timeout del socket en segundos para conectar/operar. Default 30.0." output: "dict de estado. En exito {status:'ok', conn: , mailbox: str, num_messages: int (mensajes en el buzon, de la respuesta de SELECT)}. En fallo (host vacio, auth invalida, red caida, buzon inexistente) {status:'error', error: str} y SIN clave conn." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_py_core" imports: [] tested: false tests: [] test_file_path: "" file_path: "python/functions/infra/imap_connect.py" --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from infra import imap_connect # App-password de Gmail (16 chars, requiere 2FA). Pasalo desde pass/vault. res = imap_connect( host="imap.gmail.com", port=993, user="gutierenmanuel15@gmail.com", password="abcd efgh ijkl mnop", # app-password Gmail mailbox="INBOX", ) print(res["status"]) # "ok" print(res["num_messages"]) # p.ej. 1423 conn = res["conn"] # objeto vivo: pasalo a imap_search / imap_fetch_message # ... operar ... conn.logout() # cierra siempre al terminar ``` ## Cuando usarla Usala como PRIMER paso de cualquier flujo de lectura de correo por IMAP: antes de listar carpetas (`imap_list_mailboxes`), buscar (`imap_search`) o leer un mensaje (`imap_fetch_message`). Es la fabrica del objeto `conn` que el resto del grupo consume. Para Gmail usa los defaults (`imap.gmail.com:993`); para otros proveedores cambia `host`/`port` y pasa user+pass. ## Gotchas - Funcion impura: hace red. No determinista (latencia, disponibilidad del servidor). Nunca lanza: comprueba `status` antes de tocar `conn`. - El objeto `conn` VIVO viaja dentro del dict a proposito: este grupo se compone en heredocs Python, no por `fn run` (un proceso `fn run` no puede devolver un socket abierto entre invocaciones). Mantén `conn` en memoria del mismo proceso mientras lo uses. - Cierra SIEMPRE con `conn.logout()` al terminar (o en un `finally`). Una conexion sin cerrar deja sesiones colgando en el servidor; Gmail limita el numero de conexiones IMAP simultaneas por cuenta. - Auth = app-password, NO OAuth. En Gmail debes tener 2FA activado y generar una app-password; la contrasena normal de la cuenta NO funciona por IMAP. - Si `select(mailbox)` falla (buzon inexistente, mayusculas mal en nombres tipo `[Gmail]/Sent Mail`), se hace `logout` y se devuelve `status:'error'`. - `use_ssl=False` envia credenciales en claro: usalo solo contra servidores de test en redes de confianza.