Google account management utilities for bulk cleanup of Google Drive and Gmail.
Licensed under the MIT License.
| Command | Description |
|---|---|
delete-by-date |
Permanently delete Google Drive files created on a specific date |
gmail-save-attachments |
Download Gmail attachments locally and strip them from emails |
gmail-delete-old |
Permanently delete old Promotions / Social emails in bulk |
- uv — install with:
curl -LsSf https://astral.sh/uv/install.sh | sh - Python 3.14 or later (uv will manage this automatically)
git clone <repo-url> ~/repos/google_clean
cd ~/repos/google_clean
uv syncThe google-clean command is now available via uv run google-clean.
All subcommands authenticate via OAuth2 using a credentials file you download from Google Cloud Console. This is a one-time setup.
- Go to https://console.cloud.google.com/
- Click Select a project > New Project, give it a name, click Create
In your project, go to APIs & Services > Library and enable:
| Subcommand | API to enable |
|---|---|
delete-by-date |
Google Drive API |
gmail-save-attachments |
Gmail API |
gmail-delete-old |
Gmail API |
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth 2.0 Client ID
- Application type: Desktop app
- Give it a name (e.g.
google-clean) and click Create - Click Download JSON on the resulting screen
mkdir -p ~/.config/google_clean
mv ~/Downloads/client_secret_*.json ~/.config/google_clean/credentials.jsonSecurity note:
credentials.jsonand all token files are stored in~/.config/google_clean/— outside the repository. The.gitignoreblocks anycredentials.jsonortoken*.jsonfrom being committed even if placed in the project directory.
On the first run of any subcommand, a browser window will open asking you to
log in to Google and grant permission. After you approve, a token file is saved
to ~/.config/google_clean/ and subsequent runs will not require a browser.
Tokens are stored per service:
~/.config/google_clean/token_drive.json— Drive subcommands~/.config/google_clean/token_gmail.json— Gmail subcommands
Permanently deletes all Google Drive files created on a specific date (UTC). Files are not moved to Trash — deletion is immediate and irreversible.
# Dry run — lists matching files, no changes made
uv run google-clean delete-by-date --date 2024-04-30
# Execute — permanently delete matching files
uv run google-clean delete-by-date --date 2024-04-30 --executeOptions:
| Flag | Required | Description |
|---|---|---|
--date YYYY-MM-DD |
Yes | Target creation date (UTC) |
--execute |
No | Perform deletion. Omit for dry run. |
Scans all Gmail messages for attachments, downloads qualifying files to a local folder organised by sender and date, then strips the attachment from the original email (replacing it with a text placeholder) to reclaim storage.
Also strips large CID-referenced inline images from email bodies without saving them locally.
Filtering rules:
| Type | Saved locally? | Stripped from email? | Threshold |
|---|---|---|---|
Attachment (Content-Disposition: attachment) |
Yes | Yes | ≥ 250 KB |
Inline image (CID-referenced, image/*) |
No | Yes | ≥ 5 KB |
| Everything else | No | No | — |
Output folder structure:
<output>/
└── sender@example.com/
└── 2024-03-15/
├── report.pdf
└── photo.jpg
# Dry run — lists what would be saved/stripped, no changes made
uv run google-clean gmail-save-attachments
# Execute — save attachments and strip from emails
uv run google-clean gmail-save-attachments --execute
# Test on a single sender first (recommended before running on full account)
uv run google-clean gmail-save-attachments --from sender@example.com
uv run google-clean gmail-save-attachments --from sender@example.com --execute
# Override defaults
uv run google-clean gmail-save-attachments \
--account your.account@gmail.com \
--output /path/to/attachments \
--executeOptions:
| Flag | Default | Description |
|---|---|---|
--account EMAIL |
your.account@gmail.com |
Gmail account to operate on |
--output DIR |
~/google_clean_attachments |
Root folder for saved attachments |
--from EMAIL |
(all senders) | Restrict to emails from this sender |
--execute |
— | Perform changes. Omit for dry run. |
Note: Stripping an attachment is irreversible. The saved local file will be your only copy. Always do a dry run first.
Permanently bulk-deletes Gmail emails in the Promotions and/or Social
categories that are older than a specified number of days. Uses
messages.batchDelete (1,000 emails per request) for efficiency.
# Dry run — shows counts with overlap breakdown, no deletion
uv run google-clean gmail-delete-old
# Execute — permanently delete Promotions + Social older than 30 days
uv run google-clean gmail-delete-old --execute
# Custom age threshold
uv run google-clean gmail-delete-old --days 60 --execute
# Single category only
uv run google-clean gmail-delete-old --labels promotions --execute
uv run google-clean gmail-delete-old --labels social --executeOptions:
| Flag | Default | Description |
|---|---|---|
--account EMAIL |
your.account@gmail.com |
Gmail account to operate on |
--days N |
30 |
Delete emails older than N days |
--labels LABEL ... |
promotions social |
One or more of: promotions, social |
--execute |
— | Perform deletion. Omit for dry run. |
Note: Deletion is permanent — emails will not go to Trash. The command always requires you to type
DELETEto confirm before proceeding, even with--execute.
The default account and output path are defined as constants at the top of each module and can be overridden at runtime with CLI flags:
| Constant | File | CLI flag |
|---|---|---|
DEFAULT_ACCOUNT |
gmail_save_attachments.py, gmail_delete_old.py |
--account |
DEFAULT_OUTPUT |
gmail_save_attachments.py |
--output |
DEFAULT_DAYS |
gmail_delete_old.py |
--days |
MIN_ATTACHMENT_BYTES |
gmail_save_attachments.py |
(edit source) |
MIN_INLINE_IMAGE_BYTES |
gmail_save_attachments.py |
(edit source) |
All OAuth tokens are stored in ~/.config/google_clean/ and are never inside
the repository. To force re-authentication, delete the relevant token file:
rm ~/.config/google_clean/token_gmail.json # re-auth Gmail
rm ~/.config/google_clean/token_drive.json # re-auth Drivecredentials.jsonandtoken*.jsonare blocked by.gitignore- All sensitive files live in
~/.config/google_clean/outside the repo - No credentials, tokens, email addresses, or personal paths are stored in source code
- The Gmail subcommands require the
https://mail.google.com/scope (full access), which Google marks as restricted. For personal use this is fine — Google will show a warning screen on first login which you can click through.