--- name: imap_move_message kind: function lang: py domain: infra version: "1.0.0" purity: impure signature: "def imap_move_message(conn, uid: int, dest_mailbox: str) -> dict" description: "Mueve un mensaje IMAP (por UID) del mailbox seleccionado a dest_mailbox sobre una conexion imaplib.IMAP4_SSL ya autenticada. Intenta primero conn.uid('MOVE', str(uid), dest_mailbox) (RFC 6851, atomico, soportado por Gmail) detectando la capability en conn.capabilities; si el servidor NO anuncia MOVE o el comando falla, usa el fallback clasico equivalente: UID COPY a dest + UID STORE +FLAGS (\\Deleted) en origen + EXPUNGE. Devuelve el camino usado en method ('move' o 'copy_delete'). Opera siempre por UID (estable dentro del mailbox), nunca por numero de secuencia. No abre la conexion ni resuelve credenciales: el caller pasa conn ya conectado, autenticado y con conn.select() del mailbox origen hecho. Nunca lanza: devuelve {status:'ok', uid, dest, method} o {status:'error', error}. En Gmail los nombres de carpeta llevan el prefijo '[Gmail]/' y mover a '[Gmail]/Trash' = papelera. Parte del grupo email/imap. Solo stdlib (imaplib)." tags: [email, imap, mail, move, mailbox, infra] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_py_core" imports: [imaplib] params: - name: conn desc: "objeto imaplib.IMAP4_SSL (o IMAP4) YA conectado, autenticado y con el mailbox ORIGEN seleccionado (conn.select('INBOX')). Normalmente lo produce imap_connect. La funcion no lo abre ni lo cierra. Lee conn.capabilities para decidir MOVE vs fallback." - name: uid desc: "UID del mensaje en el mailbox origen. Operacion siempre por UID (estable mientras no cambie la UIDVALIDITY), nunca por numero de secuencia." - name: dest_mailbox desc: "nombre del mailbox destino. En Gmail lleva el prefijo '[Gmail]/' (ej. '[Gmail]/Trash' = papelera, '[Gmail]/Spam'); una carpeta de usuario es solo su nombre. El destino debe existir en el servidor." output: "dict. En exito: {status:'ok', uid:int, dest:str, method:'move'|'copy_delete'} donde method indica si se uso UID MOVE atomico o el fallback COPY+STORE \\Deleted+EXPUNGE. En error (sin lanzar): {status:'error', error:str}, p.ej. si COPY o STORE devuelven un typ distinto de OK." tested: false tests: [] test_file_path: "" file_path: "python/functions/infra/imap_move_message.py" --- ## Ejemplo ```python import sys sys.path.insert(0, "python/functions") from infra.imap_connect import imap_connect from infra.imap_move_message import imap_move_message # conn ya conectado, autenticado y con INBOX seleccionado (lo produce imap_connect). conn = imap_connect(...)["conn"] # firma exacta la define imap_connect # Mover el mensaje UID 12345 a la papelera de Gmail (nota el prefijo [Gmail]/). print(imap_move_message(conn, 12345, "[Gmail]/Trash")) # {'status': 'ok', 'uid': 12345, 'dest': '[Gmail]/Trash', 'method': 'move'} # En un servidor sin MOVE el resultado seria identico salvo method: # {'status': 'ok', 'uid': 12345, 'dest': 'Archive', 'method': 'copy_delete'} ``` ## Cuando usarla Cuando quieres reubicar un mensaje entre carpetas sin descargarlo ni reenviarlo: archivar, clasificar en una etiqueta/carpeta, o enviarlo a la papelera. Es la via correcta para "borrar de verdad" en Gmail (mover a `[Gmail]/Trash`), donde marcar `\\Deleted` solo quita la etiqueta de la carpeta actual. La funcion abstrae la diferencia entre servidores con y sin soporte MOVE, asi que el caller no necesita saber si el backend implementa RFC 6851. Compone tras `imap_search`/`imap_fetch_message`. ## Gotchas - **Impura**: cambia estado en el servidor de forma persistente y puede borrar el mensaje del origen (en el fallback, tras EXPUNGE). - **Gmail \\Deleted vs Trash**: en Gmail, marcar `\\Deleted` NO borra el mensaje, solo le quita la etiqueta de la carpeta actual. Para borrar de verdad hay que mover a `[Gmail]/Trash`. Por eso esta funcion es la herramienta de borrado real en Gmail (con `dest_mailbox="[Gmail]/Trash"`). - **Prefijo [Gmail]/**: las carpetas del sistema de Gmail llevan el prefijo `[Gmail]/` (`[Gmail]/Trash`, `[Gmail]/Spam`, `[Gmail]/Drafts`, `[Gmail]/Sent Mail`, `[Gmail]/All Mail`). Las carpetas/etiquetas de usuario van por su nombre directo. - **MOVE vs COPY+EXPUNGE**: MOVE (RFC 6851) es atomico; el fallback COPY+STORE \\Deleted+EXPUNGE no lo es — si falla entre pasos, el mensaje puede quedar duplicado (en origen y destino) o marcado \\Deleted sin expurgar. El EXPUNGE del fallback materializa los borrados pendientes del mailbox origen completo. - **UID estable, no secuencia**: se usa siempre `conn.uid(...)`. El UID es estable dentro del mailbox mientras no cambie la UIDVALIDITY; el numero de secuencia se desplaza al borrar mensajes y por eso nunca se usa. - **Nunca lanza**: cualquier fallo (destino inexistente, conexion caida, respuesta no-OK) vuelve como `{status:'error', error:str}`.