imap_duplicados-20180827.py (Código fuente)

#!/usr/bin/env python3

# (c) 2017-2018 Jesús Cea Avión - jcea@jcea.es
# This code is licensed under AGPLv3.

import sys, os, argparse
import time, locale
import datetime

import imapclient

imap = imapclient.IMAPClient('127.0.0.1', use_uid=True, timeout=600, ssl=False)
imap.login('X', 'X')

cutpoint = datetime.datetime.now()-datetime.timedelta(days=7)

#busqueda = 'BODY.PEEK[HEADER.FIELDS (MESSSAGE-ID,DATE)]'
busqueda = 'BODY.PEEK[HEADER]'
b_busqueda = bytes(busqueda.replace('.PEEK', ''), 'utf-8')
folders = imap.list_folders()
for num, (_, _, folder) in enumerate(folders, 1):
    print(f'Procesando la carpeta "{folder}" ({num}/{len(folders)}): ', end='')
    sys.stdout.flush()

    assert imap.use_uid == True  # Nos aseguramos de que el atributo existe
    imap.use_uid = False

    # Tenemos que quedarnos con el "EXISTS", porque si no hay mensajes
    # en la carpeta y no estamos usando UID, no podemos seleccionar "*".
    last = imap.select_folder(folder)[b'EXISTS']
    msg = None
    total_mensajes = 0
    if last:
        last = imap.fetch(last, 'INTERNALDATE')
        msg = last.popitem()
        total_mensajes = msg[0]
    if msg and (msg[1][b'INTERNALDATE'] > cutpoint):
        maximo = total_mensajes
        minimo = 1
        while maximo >= minimo:
            medio = (maximo + minimo) // 2
            msg = imap.fetch(medio, 'INTERNALDATE')
            msg = msg.popitem()
            if msg[1][b'INTERNALDATE'] > cutpoint :
                maximo = medio - 1
            else:
                minimo = medio + 1

        seq = imap.fetch(f'{minimo}:*', ['UID'])
        seq = [i[b'UID'] for i in seq.values()]
    else:
        seq = []

    assert imap.use_uid == False  # Nos aseguramos de que el atributo existe
    imap.use_uid = True

    to_delete = []
    num_msgs = 0
    if seq:
        start = min(seq)
        reply = imap.fetch(f'{start}:*', busqueda)
        already_seen = set()
        for uid, values in reply.items():
            num_msgs += 1
            headers = values[b_busqueda]
            if headers in already_seen:
                to_delete.append(uid)
            else:
                already_seen.add(headers)

    print(total_mensajes, num_msgs, to_delete)
    if to_delete:
        imap.add_flags(to_delete, r'\DELETED', silent=True)
    imap.close_folder()