the beginning of the end
This commit is contained in:
125
README.md
Normal file
125
README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
```
|
||||
___ __ __ _ ____
|
||||
|_ _| \/ | /_\ | _ \
|
||||
| || \ / | /\_\\| |_) |
|
||||
| || |\/| |/ ___ \ __/
|
||||
|___|_| |_/_/ \_\_|
|
||||
|
||||
_ _ _ _ _ _____
|
||||
| \ | | | | | |/ / __|
|
||||
| \| | | | | ' /| _|
|
||||
| |\ | |_| | . \| |__
|
||||
|_| \_|\___/|_|\_\____|
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| a t o o l f o r d i g i t a l a r s o n |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// imap-nuke release: 2026 //
|
||||
// "scorched earth for your inbox" //
|
||||
// //
|
||||
// brought to you by someone who finally got tired of 47,821 unread emails //
|
||||
// //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
-[ WTF IS THIS ]----------------------------------------------------------------
|
||||
|
||||
You have n emails. You want 0 emails. Not zero after unsubscribing from
|
||||
every mailing list like some kind of COWARD. Zero. NULL. The void.
|
||||
|
||||
imap-nuke connects to your Gmail over IMAP/TLS and deletes every single
|
||||
message in every single folder until there is nothing left. No filter. No
|
||||
mercy. No "are you sure you want to delete this memory from 2009?" - well,
|
||||
one confirmation prompt, but that's it, and you have to type it in ALL CAPS
|
||||
like you mean it.
|
||||
|
||||
|
||||
-[ HOW IT WORKS ]---------------------------------------------------------------
|
||||
|
||||
[your gmail] [imap-nuke] [google's servers]
|
||||
| | |
|
||||
| u type ur password | |
|
||||
|<----------------------------| |
|
||||
| |--- TLS connect ---------->|
|
||||
| |--- LOGIN ---------------->|
|
||||
| |<-- OK yeah whatever ------|
|
||||
| |--- LIST "" * ------------>|
|
||||
| |<-- here's 40 folders -----|
|
||||
| "type NUKE to confirm" | |
|
||||
|<----------------------------| |
|
||||
|--- "NUKE" ----------------->| |
|
||||
| |--- SELECT [folder] ------>|
|
||||
| |--- STORE +FLAGS \Deleted->|
|
||||
| |--- EXPUNGE -------------->|
|
||||
| | (repeat for all folders) |
|
||||
| | |
|
||||
| nothing nothing
|
||||
|
||||
-[ USAGE ]----------------------------------------------------------------------
|
||||
|
||||
$ python3 nuke.py
|
||||
|
||||
============================================================
|
||||
imap-nuke - Gmail account wiper
|
||||
============================================================
|
||||
|
||||
Gmail address: doomed@gmail.com
|
||||
Password (or App Password): ****************
|
||||
|
||||
Connecting to imap.gmail.com...
|
||||
Logged in successfully.
|
||||
|
||||
Found 12 folder(s):
|
||||
INBOX
|
||||
[Gmail]/Sent Mail
|
||||
[Gmail]/Drafts
|
||||
[Gmail]/Spam
|
||||
[Gmail]/Trash
|
||||
[Gmail]/All Mail
|
||||
... and more
|
||||
|
||||
WARNING: This will PERMANENTLY delete every message in every folder.
|
||||
Account : doomed@gmail.com
|
||||
|
||||
Type "NUKE" to confirm, anything else to abort: NUKE
|
||||
|
||||
Nuking 'INBOX'... 12847 message(s) deleted.
|
||||
Nuking '[Gmail]/Sent Mail'... 3291 message(s) deleted.
|
||||
Nuking '[Gmail]/Drafts'... 17 message(s) deleted.
|
||||
Nuking '[Gmail]/Spam'... 4012 message(s) deleted.
|
||||
Nuking '[Gmail]/Trash'... 839 message(s) deleted.
|
||||
Nuking '[Gmail]/All Mail'... 0 message(s) deleted.
|
||||
|
||||
Done. 21006 message(s) deleted across 12 folder(s).
|
||||
|
||||
[Gmail]/All Mail is always processed LAST. Gmail is sneaky about restoring
|
||||
messages via All Mail if you nuke it first. We outsmart it.
|
||||
|
||||
|
||||
-[ WARNINGS ]-------------------------------------------------------------------
|
||||
|
||||
/!\ THIS IS IRREVERSIBLE /!\
|
||||
|
||||
There is no undo. There is no "recently deleted" after expunge.
|
||||
Google may retain copies on their end for some period - that's
|
||||
between you and Google. From YOUR account's perspective: gone.
|
||||
|
||||
Do not run this on an account you want to keep.
|
||||
Do not run this on someone else's account.
|
||||
Do not come crying.
|
||||
|
||||
-=[ EOF ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
|
||||
"delete it before they screenshot it"
|
||||
|
||||
imap-nuke is free software. use it, fork it, don't blame anyone.
|
||||
no warranty. no support. no prisoners.
|
||||
|
||||
the inbox is a lie
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
```
|
||||
147
nuke.py
Executable file
147
nuke.py
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
imap-nuke: Permanently delete every message in a Gmail account via IMAP.
|
||||
|
||||
Prerequisites:
|
||||
- IMAP must be enabled in Gmail settings (Settings -> See all settings -> Forwarding and POP/IMAP)
|
||||
- If your account uses 2-Step Verification, generate an App Password:
|
||||
Google Account -> Security -> 2-Step Verification -> App passwords
|
||||
Use that 16-character password instead of your real password.
|
||||
"""
|
||||
|
||||
import imaplib
|
||||
import sys
|
||||
import getpass
|
||||
import socket
|
||||
|
||||
IMAP_HOST = "imap.gmail.com"
|
||||
IMAP_PORT = 993
|
||||
BATCH_SIZE = 500
|
||||
|
||||
|
||||
def connect(email: str, password: str) -> imaplib.IMAP4_SSL:
|
||||
print(f"Connecting to {IMAP_HOST}...")
|
||||
try:
|
||||
conn = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
|
||||
except (socket.gaierror, OSError) as e:
|
||||
sys.exit(f"Connection failed: {e}")
|
||||
|
||||
try:
|
||||
conn.login(email, password)
|
||||
except imaplib.IMAP4.error as e:
|
||||
sys.exit(f"Login failed: {e}")
|
||||
|
||||
print("Logged in successfully.")
|
||||
return conn
|
||||
|
||||
|
||||
def list_folders(conn: imaplib.IMAP4_SSL) -> list[str]:
|
||||
status, data = conn.list()
|
||||
if status != "OK":
|
||||
sys.exit("Failed to list folders.")
|
||||
|
||||
folders = []
|
||||
for item in data:
|
||||
if item is None:
|
||||
continue
|
||||
# IMAP LIST response: (\Flags) "delimiter" "name" or (\Flags) "delimiter" name
|
||||
decoded = (item.decode() if isinstance(item, bytes) else item).strip()
|
||||
if decoded.endswith('"'):
|
||||
# Quoted name: extract between the last two double-quotes
|
||||
name = decoded.rsplit('"', 2)[-2]
|
||||
else:
|
||||
# Unquoted name: last whitespace-separated token
|
||||
name = decoded.rsplit(None, 1)[-1]
|
||||
if name:
|
||||
folders.append(name)
|
||||
|
||||
return folders
|
||||
|
||||
|
||||
def nuke_folder(conn: imaplib.IMAP4_SSL, folder: str) -> int:
|
||||
"""Mark all messages in a folder as deleted and expunge. Returns count deleted."""
|
||||
status, _ = conn.select(f'"{folder}"', readonly=False)
|
||||
if status != "OK":
|
||||
print(f" Skipping {folder!r} (could not select)")
|
||||
return 0
|
||||
|
||||
# Search for all messages
|
||||
status, data = conn.search(None, "ALL")
|
||||
if status != "OK" or not data or not data[0]:
|
||||
return 0
|
||||
|
||||
msg_ids = data[0].split()
|
||||
if not msg_ids:
|
||||
return 0
|
||||
|
||||
count = len(msg_ids)
|
||||
print(f"\n {count} message(s) found.", flush=True)
|
||||
# Mark and expunge in batches to avoid server timeouts on large folders
|
||||
deleted = 0
|
||||
for i in range(0, len(msg_ids), BATCH_SIZE):
|
||||
batch = msg_ids[i:i + BATCH_SIZE]
|
||||
id_range = b",".join(batch)
|
||||
conn.store(id_range, "+FLAGS", "\\Deleted")
|
||||
conn.expunge()
|
||||
deleted += len(batch)
|
||||
print(f" {deleted}/{count} deleted...", flush=True)
|
||||
return count
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 60)
|
||||
print(" imap-nuke - Gmail account wiper")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
email = input("Gmail address: ").strip()
|
||||
if not email:
|
||||
sys.exit("No email provided.")
|
||||
|
||||
password = getpass.getpass("Password (or App Password): ")
|
||||
if not password:
|
||||
sys.exit("No password provided.")
|
||||
|
||||
conn = connect(email, password)
|
||||
|
||||
folders = list_folders(conn)
|
||||
print(f"\nFound {len(folders)} folder(s):")
|
||||
for f in folders:
|
||||
print(f" {f}")
|
||||
|
||||
print()
|
||||
print("WARNING: This will PERMANENTLY delete every message in every folder.")
|
||||
print(f"Account : {email}")
|
||||
confirm = input('\nType "NUKE" to confirm, anything else to abort: ').strip()
|
||||
if confirm != "NUKE":
|
||||
print("Aborted.")
|
||||
conn.logout()
|
||||
sys.exit(0)
|
||||
|
||||
print()
|
||||
total_deleted = 0
|
||||
|
||||
# Process [Gmail]/All Mail last - deleting from individual labels first
|
||||
# avoids Gmail re-creating messages from All Mail.
|
||||
priority_last = {"[Gmail]/All Mail", "[Google Mail]/All Mail"}
|
||||
ordered = [f for f in folders if f not in priority_last] + \
|
||||
[f for f in folders if f in priority_last]
|
||||
|
||||
for folder in ordered:
|
||||
print(f"Nuking {folder!r}...", end="", flush=True)
|
||||
try:
|
||||
count = nuke_folder(conn, folder)
|
||||
except imaplib.IMAP4.abort as e:
|
||||
print(f"\n Connection dropped ({e}), reconnecting...")
|
||||
conn = connect(email, password)
|
||||
count = nuke_folder(conn, folder)
|
||||
total_deleted += count
|
||||
print(f" Done ({count} message(s) deleted).")
|
||||
|
||||
print()
|
||||
print(f"Done. {total_deleted} message(s) deleted across {len(folders)} folder(s).")
|
||||
conn.logout()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user