Last active
February 15, 2026 22:16
-
-
Save lucahammer/1aa16b4d3c1fb04035839da5ef699d65 to your computer and use it in GitHub Desktop.
Delete all your Tweets Javascript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| This may get your account banned. It runs in your regular browser with your regular login without needing the API. | |
| The script does the same things that you would do yourself: | |
| Click the three dots, select delete Tweet, confirm, scroll to next Tweet, repeat. | |
| ========================== | |
| Usage | |
| 1. Open your Twitter profile in a browser | |
| 2. Open the console in the developer tools (F12) | |
| 3. Paste the script and press enter | |
| 4. ??? | |
| 5. Tweets are gone. (At least from your profile. They may still exist in backups.) | |
| ========================== | |
| Copyright 2023 Nobody | |
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| */ | |
| const waitForElemToExist = async (selector) => { | |
| return new Promise(resolve => { | |
| if (document.querySelector(selector)) { | |
| return resolve(document.querySelector(selector)); | |
| } | |
| const observer = new MutationObserver(() => { | |
| if (document.querySelector(selector)) { | |
| resolve(document.querySelector(selector)); | |
| observer.disconnect(); | |
| } | |
| }); | |
| observer.observe(document.body, { | |
| subtree: true, | |
| childList: true, | |
| }); | |
| }); | |
| } | |
| const deleteTweets = async () => { | |
| const more = '[data-testid="tweet"] [aria-label="More"][data-testid="caret"]' | |
| while (document.querySelectorAll(more).length > 0) { | |
| // give the Tweets a chance to load; increase/decrease if necessary | |
| // afaik the limit is 50 requests per minute | |
| await new Promise(r => setTimeout(r, 1200)); | |
| // hide recommended profiles and stuff | |
| document.querySelectorAll('[aria-label="Profile timelines"]+section [data-testid="cellInnerDiv"]>div>div>div').forEach(x => x.remove()) | |
| document.querySelectorAll('[aria-label="Profile timelines"]+section [data-testid="cellInnerDiv"]>div>div>[role="link"]').forEach(x => x.remove()) | |
| document.querySelector('[aria-label="Profile timelines"]').scrollIntoView({ 'behavior': 'smooth' }) | |
| // if it is a Fav, unfav it (only works if script is executed on Likes tab) | |
| unfav = document.querySelector('[data-testid="unlike"]') | |
| if (unfav) { | |
| unfav.click() | |
| document.querySelector('[data-testid="tweet"]').remove() | |
| } | |
| // if it is a Retweet, unretweet it | |
| unretweet = document.querySelector('[data-testid="unretweet"]') | |
| if (unretweet) { | |
| unretweet.click() | |
| confirmURT = await waitForElemToExist('[data-testid="unretweetConfirm"]') | |
| confirmURT.click() | |
| } | |
| // delete Tweet | |
| else { | |
| caret = await waitForElemToExist(more) | |
| caret.click() | |
| menu = await waitForElemToExist('[role="menuitem"]'); | |
| if (menu.textContent.includes('@')) { | |
| // don't unfollow people (because their Tweet is the reply tab) | |
| caret.click() | |
| document.querySelector('[data-testid="tweet"]').remove() | |
| } | |
| else { | |
| menu.click(); | |
| confirmation = document.querySelector('[data-testid="confirmationSheetConfirm"]') | |
| if (confirmation) confirmation.click() | |
| } | |
| } | |
| del_count++ | |
| // print to the console how many Tweets already got deleted | |
| // Change the 10 to how often you want an update. | |
| // 10 for every 10th Tweet, 1 for every Tweet, 100 for every 100th Tweet | |
| if (del_count % 10 == 0) console.log(`${new Date().toUTCString()} Deleted ${del_count} Tweets`) | |
| } | |
| console.log('Switching to Replies.') | |
| document.querySelectorAll('[aria-label="Profile timelines"]>div>div>div>div>a')[1].click() | |
| await new Promise(r => setTimeout(r, 2000)); | |
| if (document.querySelectorAll(more).length > 0) { | |
| deleteTweets(); | |
| } | |
| else { | |
| console.log('Switching to Tweets.') | |
| document.querySelectorAll('[aria-label="Profile timelines"]>div>div>div>div>a')[0].click() | |
| await new Promise(r => setTimeout(r, 2000)); | |
| if (document.querySelectorAll(more).length > 0) { | |
| deleteTweets(); | |
| } | |
| } | |
| console.log('No Tweets left. Please reload to confirm.') | |
| } | |
| del_count = 0; | |
| deleteTweets(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
// Configuration - Human-like timing
const CONFIG = {
MIN_ACTION_DELAY: 3000, // minimum 3 seconds between actions
MAX_ACTION_DELAY: 8000, // maximum 8 seconds between actions
MIN_CLICK_DELAY: 500, // minimum delay between clicks
MAX_CLICK_DELAY: 1500, // maximum delay between clicks
TAB_SWITCH_DELAY: 4000, // wait 4 seconds after switching tabs
PAUSE_EVERY: 15, // take a break every N deletions
PAUSE_MIN: 10000, // minimum pause duration (10 seconds)
PAUSE_MAX: 25000, // maximum pause duration (25 seconds)
LOG_FREQUENCY: 5, // log every 5 deletions
WAIT_TIMEOUT: 15000, // max wait time for elements
SCROLL_VARIANCE: 3 // random scroll count
};
const SELECTORS = {
MORE_BUTTON: '[data-testid="tweet"] [aria-label="More"][data-testid="caret"]',
UNLIKE_BUTTON: '[data-testid="unlike"]',
UNRETWEET_BUTTON: '[data-testid="unretweet"]',
UNRETWEET_CONFIRM: '[data-testid="unretweetConfirm"]',
DELETE_CONFIRM: '[data-testid="confirmationSheetConfirm"]',
TWEET: '[data-testid="tweet"]',
MENU_ITEM: '[role="menuitem"]',
PROFILE_TIMELINES: '[aria-label="Profile timelines"]',
TAB_LINKS: '[aria-label="Profile timelines"]>div>div>div>div>a'
};
// Generate random delay within range (more human-like)
const randomDelay = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
// Human-like delay with variance
const humanDelay = async (baseMin = CONFIG.MIN_ACTION_DELAY, baseMax = CONFIG.MAX_ACTION_DELAY) => {
const delay = randomDelay(baseMin, baseMax);
await new Promise(r => setTimeout(r, delay));
};
// Improved element waiter with timeout and cleanup
const waitForElemToExist = async (selector, timeout = CONFIG.WAIT_TIMEOUT) => {
return new Promise((resolve, reject) => {
const element = document.querySelector(selector);
if (element) {
return resolve(element);
}
};
// Human-like click with random delay
const humanClick = async (element) => {
if (element) {
// Random delay before click (simulating mouse movement time)
await new Promise(r => setTimeout(r, randomDelay(100, 400)));
element.click();
// Random delay after click (simulating human reaction time)
await new Promise(r => setTimeout(r, randomDelay(CONFIG.MIN_CLICK_DELAY, CONFIG.MAX_CLICK_DELAY)));
}
};
// Human-like scrolling behavior
const humanScroll = async () => {
const scrollCount = randomDelay(1, CONFIG.SCROLL_VARIANCE);
for (let i = 0; i < scrollCount; i++) {
const timeline = document.querySelector(SELECTORS.PROFILE_TIMELINES);
if (timeline) {
timeline.scrollIntoView({ behavior: 'smooth' });
await new Promise(r => setTimeout(r, randomDelay(500, 1200)));
}
}
};
// Helper to remove clutter with human-like behavior
const removeClutter = async () => {
const clutterSelectors = [
'[aria-label="Profile timelines"]+section [data-testid="cellInnerDiv"]>div>div>div',
'[aria-label="Profile timelines"]+section [data-testid="cellInnerDiv"]>div>div>[role="link"]'
];
};
// Take a human-like break▶️ Resuming...');
const takeBreak = async (count) => {
if (count > 0 && count % CONFIG.PAUSE_EVERY === 0) {
const pauseDuration = randomDelay(CONFIG.PAUSE_MIN, CONFIG.PAUSE_MAX);
console.log(
⏸️ Taking a ${Math.round(pauseDuration/1000)}s break (appears more human)...);await new Promise(r => setTimeout(r, pauseDuration));
console.log('
}
};
// Handle unlike action
const handleUnlike = async () => {
const unfavButton = document.querySelector(SELECTORS.UNLIKE_BUTTON);
if (unfavButton) {
await humanClick(unfavButton);
await new Promise(r => setTimeout(r, randomDelay(800, 1500)));
const tweet = document.querySelector(SELECTORS.TWEET);
if (tweet) tweet.remove();
return true;
}
return false;
};
// Handle unretweet action⚠️ Failed to confirm unretweet:', e.message);
const handleUnretweet = async () => {
const unretweetButton = document.querySelector(SELECTORS.UNRETWEET_BUTTON);
if (unretweetButton) {
await humanClick(unretweetButton);
try {
const confirmButton = await waitForElemToExist(SELECTORS.UNRETWEET_CONFIRM, 5000);
await humanClick(confirmButton);
await new Promise(r => setTimeout(r, randomDelay(800, 1500)));
return true;
} catch (e) {
console.warn('
}
}
return false;
};
// Handle delete tweet action
const handleDelete = async () => {
try {
const caret = await waitForElemToExist(SELECTORS.MORE_BUTTON, 5000);
await humanClick(caret);
};
// Switch to a specific tab with human-like delay
const switchTab = async (tabIndex, tabName) => {
console.log(
🔄 Switching to ${tabName}...);const tabs = document.querySelectorAll(SELECTORS.TAB_LINKS);
if (tabs[tabIndex]) {
await humanClick(tabs[tabIndex]);
// Extra wait for tab to load content
await new Promise(r => setTimeout(r, CONFIG.TAB_SWITCH_DELAY + randomDelay(0, 2000)));
return true;
}
return false;
};
// Calculate and display estimated time
const displayEstimate = (count) => {
const avgTime = (CONFIG.MIN_ACTION_DELAY + CONFIG.MAX_ACTION_DELAY) / 2;
const estimatedSeconds = Math.round((count * avgTime) / 1000);
const hours = Math.floor(estimatedSeconds / 3600);
const minutes = Math.floor((estimatedSeconds % 3600) / 60);
};
// Main deletion logic
const deleteTweets = async () => {
let deleteCount = 0;
const startTime = Date.now();
};
// Start the process with a countdown
console.log('🤖 Human-speed tweet deletion script loaded.');
console.log('⚡ Settings: 3-8 second delays, breaks every 15 items');
console.log('⏳ Starting in 5 seconds...');
setTimeout(() => {
console.log('3...');
setTimeout(() => {
console.log('2...');
setTimeout(() => {
console.log('1...');
setTimeout(() => deleteTweets(), 1000);
}, 1000);
}, 1000);
}, 2000);