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