Skip to content

Instantly share code, notes, and snippets.

@blalor
Created November 7, 2015 11:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save blalor/52853f78da15516c292a to your computer and use it in GitHub Desktop.
Save blalor/52853f78da15516c292a to your computer and use it in GitHub Desktop.
Bulk move messages from All Mail to Trash

I hate GMail, but sometimes you gotta. They're notoriously awful at deleting lots of messages at once, as their own search engine will tell you. I had more than 1,000,000 messages I needed to purge and couldn't do it using their own tools. So I wrote my own.

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
## http://nickblogus.tumblr.com/post/73553186015/gmail-deleting-big-amount-of-old-messages-problem
import imaplib
from email.parser import HeaderParser
from email.utils import parsedate_tz, mktime_tz
from datetime import datetime, timedelta
def chunks(arr, num):
for i in xrange(0, len(arr), num):
yield arr[i:i + num]
def check_result(command, resp, result):
assert resp == "OK", "%s failed with %s: %s" % (command, resp, result)
return result
def main(host, userid, password, days):
age_threshold = timedelta(days=int(days))
now = datetime.now()
print "connecting"
box = imaplib.IMAP4_SSL(host, 993)
print "logging in"
check_result("logging in", *box.login(userid, password))
print "selecting"
check_result("selecting", *box.select("[Gmail]/All Mail"))
print "searching"
result = check_result("searching", *box.search(None, '(BEFORE "%s")' % (now - age_threshold).strftime("%d-%b-%Y")))
uids = result[0].split()
print "%d messages in result" % len(uids)
total_deleted = 0
chunk_size = 250
for chunk in chunks(uids, chunk_size):
deleted = False
for attempt in range(0, 3):
try:
print "deleting %d, attempt %d" % (len(chunk), attempt)
check_result("storing", *box.store(",".join(chunk), "+X-GM-LABELS", "\\Trash"))
deleted = True
total_deleted += chunk_size
break
except AssertionError, e:
print "attempt %d failed: %s" % (attempt, e)
assert deleted, "unable to delete after multiple attempts"
print "deleted %6.2f%%, %d of %d" % ((total_deleted * 100.0 / len(uids)), total_deleted, len(uids))
# assert box.expunge()[0] == "OK"
if __name__ == "__main__":
import sys
host, userid, password, days = sys.argv[1:]
main(host, userid, password, days)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment