Browser-driven X/Twitter post cleanup command-line tool
Project description
Twit Cleaner
A Playwright browser-navigation tool for deleting posts and replies from your own X account and undoing your reposts. Profile matching is keyword based. Scans are unlimited unless --max-posts is supplied.
Project Structure
pyproject.toml Build metadata, dependencies, and CLI entrypoints
twit_cleaner/
__main__.py Package entrypoint for python -m twit_cleaner
keyword_profiles.json Bundled politics, personal, and work profiles
app.py Application orchestration and exit codes
cli.py Arguments, shortcuts, and validation
models.py Enums and immutable runtime models
constants.py UI labels and project paths
keywords.py Profile loading and text classification
urls.py Profile and owned-status URL handling
posts.py Timeline-card inspection and type decisions
navigation.py Login and profile navigation
actions.py Delete, permalink fallback, and unretweet actions
scanner.py Sequential timeline scanning
browser.py CDP and persistent-browser lifecycle
tests/
test_domain.py Ownership, mode, CLI, and keyword contracts
Internal modules use package-relative imports. Run the installed twit-cleaner command or invoke the package directly with python -m twit_cleaner.
Setup
python3 -m venv .venv
. .venv/bin/activate
python -m pip install twit-cleaner
python -m playwright install chromium firefox
twit-cleaner --help
python -m twit_cleaner --help
Existing Chrome Login
Start Chrome with remote debugging, log in to X, and leave the window open:
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-x-cdp" --ozone-platform=x11
For Chromium:
chromium --remote-debugging-port=9222 --user-data-dir="$HOME/.chromium-x-cdp" --ozone-platform=x11
Confirm that the connection works:
curl http://127.0.0.1:9222/json/version
Warning: Delete Everything
Unsafe and irreversible: this command makes one interleaved pass through /with_replies. For each card, it undoes an active repost or deletes the card when its primary permalink belongs to your profile. It does not inspect political content and does not ask for confirmation in the terminal. X still displays its normal per-item confirmation dialog, which the tool accepts automatically.
With your remote-debugging Chrome window open and logged in, run:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --delete-all
Reposts, replies, and original posts are handled in the order they appear. Each item is processed sequentially: find one, choose Undo repost or Delete, act on it, query the updated /with_replies timeline, then find the next one. If one of your own posts is also reposted, the tool undoes the repost first, then sees the same owned permalink again and deletes the post. Press Ctrl+C to stop.
Before every removal, the tool checks for an active unretweet control. That control means your logged-in account reposted the item; the original post may belong to any account. Those items use Undo repost without requiring the original author's permalink to match your handle. Only post and reply deletion requires a permalink matching /YOUR_HANDLE/status/..., which prevents conversation cards from other accounts from being deleted. The tool first tries the timeline menu; if that fails, it opens the owned permalink in a temporary tab and retries there.
Match And Exclusion Profiles
The built-in politics, personal, and work profiles can be used for either matching or exclusion:
--match PROFILEselects content to delete.--exclude-mode PROFILEpreserves matching owned posts and replies.- Exclusions never prevent the tool from undoing your reposts.
Delete personal posts while preserving work posts:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match personal --exclude-mode work --delete
Delete work replies while preserving political replies:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target replies --match work --exclude-mode politics --delete
The built-in terms live in twit_cleaner/keyword_profiles.json. Use --keyword-profiles PATH to replace all three built-in profiles.
Custom Keyword Files
Custom terms are stored outside the JSON. Use one file for matching and another for exclusion. Terms may be separated by spaces or newlines, are case-insensitive, and become hashtags when prefixed with #. Lines beginning with // are comments.
Edit custom_match_keywords.txt:
football gaming photography
#sports #games
Edit custom_exclusion_keywords.txt:
family portfolio birthday
#keepme #work
Use both custom files:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match custom --match-keywords-file ./custom_match_keywords.txt --exclude-mode custom --exclude-keywords-file ./custom_exclusion_keywords.txt --delete
Custom whitespace-separated files treat project and name as two terms. Put multi-word phrases in a replacement JSON profile when phrase matching is required. Keep personalized files outside site-packages so package upgrades cannot overwrite them.
Keywords On The Command Line
Use --match-keywords and --exclude-keywords for terms that do not need a file. Each shell argument is one term, so quote phrases and hashtags:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match custom --match-keywords election "prime minister" "#politics" --exclude-keywords family "family photo" "#keepme" --delete
Inline terms augment a selected built-in profile. For example, this matches the built-in work profile plus freelance:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match work --match-keywords freelance --delete
Supplying --exclude-keywords without --exclude-mode automatically uses custom exclusion mode.
Political Command Examples
Dry-run all political posts without deleting anything:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match politics
Delete all political posts:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target posts --match politics --delete
Delete all political replies:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target replies --match politics --delete
Undo all political reposts:
twit-cleaner --profile-url https://x.com/YOUR_HANDLE --connect-cdp http://127.0.0.1:9222 --target retweets --match politics --delete
All Options
-h, --help Show command help and exit.
--profile-url URL X profile URL to scan. Required.
--max-posts N Optional selected-item limit. Omit for the entire timeline.
--delete Perform matched delete/undo actions. Omit for a dry run.
--headless Run a launched browser without a visible window.
--browser NAME Browser engine: chromium or firefox. Default: chromium.
--browser-profile-dir PATH
Persistent browser profile directory.
--browser-channel NAME Chromium channel: chrome, chrome-beta, chrome-dev,
chrome-canary, msedge, or chromium.
--executable-path PATH Browser executable to launch.
--connect-cdp URL Attach to running Chrome/Chromium through CDP.
--login Open X login and wait for Enter before scanning.
--target TYPE Selected category: posts, replies, or retweets.
--match MODE Match politics, personal, work, custom, or all.
--delete-all Interleave repost undo and owned post/reply deletion.
--delete-all-posts Delete every original post regardless of content.
--delete-all-replies Delete every reply regardless of content.
--delete-all-retweets Undo every repost regardless of content.
--unretweet-all Alias for --delete-all-retweets.
--include-replies Deprecated alias for --target replies.
--match-keywords-file PATH
Add/customize match terms separated by spaces or newlines.
--keywords-file PATH Compatibility alias for --match-keywords-file.
--exclude-keywords-file PATH
Add/customize exclusion terms separated by spaces or newlines.
--match-keywords TERM [TERM ...]
Add match terms directly; quote phrases and #hashtags.
--exclude-keywords TERM [TERM ...]
Add exclusion terms directly; quote phrases and #hashtags.
--keyword-profiles PATH JSON file containing politics, personal, and work profiles.
--only-keywords-file Deprecated shortcut for --match custom.
--exclude-mode MODE Preserve politics, personal, work, or custom matches.
--pause SECONDS Delay between browser actions. Default: 0.8.
--connect-cdp cannot be combined with --login, --headless, --browser-profile-dir, --browser-channel, or --executable-path.
Notes
- Run a dry-run command before selective deletion.
--target repliesuses the profile's/with_repliestimeline.- Reposts are removed by undoing your repost, never by deleting the original author's post.
- Reposts made by the logged-in account are undone regardless of who authored the original post.
- Every destructive action re-checks the item type immediately before clicking; an unverified item is skipped.
- Post ownership is verified from the profile handle in its
/HANDLE/status/...permalink. - Failed timeline deletions are retried from the owned post's permalink in a temporary tab.
- Exclusion profiles preserve matching owned posts/replies but do not preserve reposts.
- X changes its interface often. Missing menus or confirmation buttons are reported and skipped.
- Use this tool only on accounts you control.
Keyword Profiles
The complete politics, personal, and work keyword and hashtag profiles are maintained in twit_cleaner/keyword_profiles.json. Each profile can be used with either --match or --exclude-mode. Custom terms belong in the separate match and exclusion text files.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file twit_cleaner-0.1.2.tar.gz.
File metadata
- Download URL: twit_cleaner-0.1.2.tar.gz
- Upload date:
- Size: 22.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4e2047b7edf5e2be009fcaa14b62b4cf2141d2272995e02bd3e405315474ad5
|
|
| MD5 |
4ad9bc3b0680bcc4fe40017764f0b86d
|
|
| BLAKE2b-256 |
5cf050c352da71c41cc7ac16a69add84c7c4102697cae588921b829516acd87d
|
Provenance
The following attestation bundles were made for twit_cleaner-0.1.2.tar.gz:
Publisher:
publish.yml on dezugin/twit_cleaner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
twit_cleaner-0.1.2.tar.gz -
Subject digest:
a4e2047b7edf5e2be009fcaa14b62b4cf2141d2272995e02bd3e405315474ad5 - Sigstore transparency entry: 1981136851
- Sigstore integration time:
-
Permalink:
dezugin/twit_cleaner@85c4797988d9753029aaa796a065d5876486f7cb -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/dezugin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@85c4797988d9753029aaa796a065d5876486f7cb -
Trigger Event:
release
-
Statement type:
File details
Details for the file twit_cleaner-0.1.2-py3-none-any.whl.
File metadata
- Download URL: twit_cleaner-0.1.2-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4cbdf3917fd6ddc5a584682384fc1170154f778af9bb837be46fb3472338373
|
|
| MD5 |
529a0a72e638b4ab5fca4fde0da35654
|
|
| BLAKE2b-256 |
e71a086ea76f7b5cf9393d6ec9ffc683c7f8339ec864c814ebf50a5212420552
|
Provenance
The following attestation bundles were made for twit_cleaner-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on dezugin/twit_cleaner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
twit_cleaner-0.1.2-py3-none-any.whl -
Subject digest:
b4cbdf3917fd6ddc5a584682384fc1170154f778af9bb837be46fb3472338373 - Sigstore transparency entry: 1981136986
- Sigstore integration time:
-
Permalink:
dezugin/twit_cleaner@85c4797988d9753029aaa796a065d5876486f7cb -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/dezugin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@85c4797988d9753029aaa796a065d5876486f7cb -
Trigger Event:
release
-
Statement type: