Apesar de ter me apaixonado pelo Gmail logo que foi lançado, nunca curti webmails1. Por isso, foi um alívio quando o suporte a POP2 e, posteriormente, a IMAP foram adicionados ao serviço.
Assim que o suporte a IMAP foi adicionado, não pensei duas vezes e o adotei. Só existe um probleminha: o Gmail, com seu incentivo a não apagamento de mensagens e sua ideia de labels3 (e o fato de cada label ser realizado como uma pasta) gera, em sua implementação de IMAP, um monte de mensagens duplicadas (uma para cada label mais a do "All Mail"). As consequências são mais ou menos desastrosas do ponto de vista de bandwidth e de armazenamento: se você tem 5GB de email armazenados, vai baixar, e armazenar, no mínimo 10GB. :-(
Minha solução sempre foi a de não baixar o "All Mail" via IMAP e, quando precisava buscar alguma mensagem sem label, recorria à versão web. Não muito inteligente, mas funcionava4.
No entanto, como, há cerca de um mês, decidi me livrar do Gmail, era melhor que eu desfizesse os efeitos de minha gambiarra. Notei que o Thunderbird possuía uma extensão de remoção de emails duplicados. Infelizmente, ela não aguentava tratar meus mais de 40 mil emails duplicados. A solução, é claro, foi escrever meu próprio script para realizar a tarefa5!
O script não precisava ser mega robusto ou configurável, já que eu seria, em princípio, seu único usuário. Sua função, portanto, é apagar minhas mensagens indesejadas lá do All Mail e pronto. Então, sua ideia básica é:
Depois, numa passada pelo hash, é só examinar os Message-IDs que forem mapeados para mais de uma mensagem. Em Python, eu comecei representando uma mensagem como a pasta que a contém, seu UID naquela pasta e seu Message-ID:
class Message(object):
def __init__(self, path, uid, id):
self.id = id
self.uid = uid
self.path = path
Agora, considerando que uma conexão imap autenticada já existe, obter a lista de pastas é moleza:
mboxes = connection.list()
Então é só iterar por cada uma das pastas e pegar a listagem de mensagens (como no script anterior):
for mbox in mboxes[1]:
mbox = mbox.split(' "." ')[1].replace('"', '')
ret, count = connection.select(mbox)
ret, data = connection.search(None, 'ALL') # data contém a lista de UIDs
for mnum in data[0].split():
ret, tmp = connection.fetch(mnum, 'RFC822.HEADER')
header = Parser().parsestr(tmp[0][1])
mid = header['Message-ID']
if mid in cache:
cache[mid].append(Message(mbox, mnum, mid))
else:
cache[mid] = [Message(mbox, mnum, mid)]
Ali em cima eu usei a maravilhosa classe Parser do módulo email.parser da biblioteca padrão do Python para interpretar o conteúdo do cabeçalho da mensagem (RFC822.HEADER lá em cima) e transformá-lo "em" um dicionário. Chique (e confuso), não? Pronto, agora todas as mensagens já estão em cache. Basta detonar aquelas que não quero:
allmail = 'INBOX.Gmail.All Mail' # Quero detonar esse cara
tbd = [] # to be deleted
for key in cache:
if len(cache[key]) > 1: # Duplicado!
for msg in cache[key]:
if msg.path == allmail:
tbd.append(msg.uid)
conn.select(allmail)
for uid in tbd:
# Adicionando o flag "Deleted" a todas as duplicadas
conn.store(uid, '+FLAGS', '\\Deleted')
# Efetivamente deletando-as
conn.expunge()
E é isso. O script completo, na verdade, é um pouco mais complicado que o apresentado acima, mas a ideia é a mesma. Fica aí, caso seja útil para mais alguém. Para mim, foi. Meu "All Mail" foi reduzido para um terço de seu tamanho original.