Files
fn_registry/python/functions/infra/imap_search.md
T
egutierrez 763e06c127 feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-20 18:22:23 +02:00

82 lines
3.9 KiB
Markdown

---
name: imap_search
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def imap_search(conn, criteria: str = 'UNSEEN', mailbox: str = '') -> dict"
description: "Busca mensajes en un buzon IMAP por criterio y devuelve sus UIDs. Sobre una conexion imaplib viva (de imap_connect), opcionalmente hace select(mailbox) y luego conn.uid('SEARCH', None, criteria). Usa SIEMPRE UIDs (estables mientras no cambie UIDVALIDITY), no numeros de secuencia (que se renumeran al borrar). criteria es una expresion IMAP cruda RFC 3501 (UNSEEN, ALL, FROM x, SUBJECT y, SINCE 01-Jan-2026, combinaciones). Devuelve {status:'ok', uids:[int], count} o {status:'error', error}. Nunca lanza."
tags: [email, imap, infra, search, network]
params:
- name: conn
desc: "Objeto imaplib.IMAP4[_SSL] vivo y autenticado, producido por imap_connect. None devuelve status error."
- name: criteria
desc: "Expresion de busqueda IMAP cruda (RFC 3501 SEARCH). Ej: 'UNSEEN', 'ALL', 'FROM foo@bar.com', 'SUBJECT factura', 'SINCE 01-Jan-2026', 'UNSEEN SINCE 01-Jun-2026'. Vacio devuelve status error. Default 'UNSEEN'."
- name: mailbox
desc: "Si no esta vacio, se hace select(mailbox) antes de buscar (ej. '[Gmail]/Sent Mail'). Vacio (default) usa el buzon ya seleccionado."
output: "dict de estado. En exito {status:'ok', uids: list[int], count: int}: uids son UIDs (no numeros de secuencia), ordenados como los devuelve el servidor; lista vacia si nada casa (sigue siendo status ok). En fallo (conn None, criteria vacio o mal formado, buzon inexistente) {status:'error', error: str}."
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_search.py"
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra import imap_connect, imap_search, imap_fetch_message
c = imap_connect("imap.gmail.com", 993, "gutierenmanuel15@gmail.com", "abcd efgh ijkl mnop")
conn = c["conn"]
# No leidos del INBOX (buzon ya seleccionado por imap_connect)
res = imap_search(conn, criteria="UNSEEN")
print(res["status"], res["count"]) # "ok" 7
print(res["uids"]) # [1422, 1425, 1431, ...]
# Buscar en otra carpeta sin reconectar
sent = imap_search(conn, criteria="SINCE 01-Jun-2026", mailbox="[Gmail]/Sent Mail")
# Fetch del primer UID encontrado
if res["uids"]:
msg = imap_fetch_message(conn, res["uids"][0])
print(msg["message"]["subject"])
conn.logout()
```
## Cuando usarla
Usala tras `imap_connect` cuando necesites localizar mensajes por criterio
(no leidos, de un remitente, por asunto, por fecha) antes de leerlos con
`imap_fetch_message`. Es el paso intermedio del flujo lectura: connect -> search
-> fetch. Para barrer una carpeta distinta del INBOX pasa `mailbox` y evita
una reconexion.
## Gotchas
- Funcion impura: hace red sobre el `conn` vivo. Nunca lanza: comprueba `status`.
El `conn` lo provee `imap_connect` (no resuelve credenciales).
- Devuelve UIDs, NO numeros de secuencia. Importante: guarda y reutiliza los
UIDs; los seq cambian cuando se borran mensajes, los UIDs no (salvo cambio de
UIDVALIDITY del buzon, raro). `imap_fetch_message` tambien espera UID.
- El `criteria` es sintaxis IMAP cruda, sin validar: un criterio mal formado
hace que el servidor responda no-OK y devuelve `status:'error'`. Las fechas
van en formato IMAP `DD-Mon-YYYY` (ej. `01-Jan-2026`), no ISO.
- `SEARCH` por defecto opera sobre US-ASCII; para acentos en `SUBJECT`/`FROM`
algunos servidores requieren `CHARSET UTF-8` (este wrapper pasa `None` como
charset, que cubre el caso comun). Si necesitas charset, busca por cabeceras
ASCII o filtra el resultado en cliente.
- Una busqueda sin coincidencias devuelve `status:'ok'` con `uids:[]` y
`count:0` — distingue "sin resultados" mirando `count`, no `status`.
- Cierra con `conn.logout()` al terminar (responsabilidad del caller).