Created
July 13, 2025 20:30
-
-
Save jatinkrmalik/1f3b6bcd685d00350f41c75a879b4d66 to your computer and use it in GitHub Desktop.
Client-side browser script that celetes tweets from your profile posted after a specific date and time while respecting rate limiter
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
/** | |
* @description Deletes tweets from your profile posted after a specific date and time. | |
* @instructions Paste this entire script into your browser's developer console | |
* on your Twitter/X profile page (e.g., https://x.com/your-username) | |
* and press Enter. | |
* DISCLAIMER: USE AT YOUR OWN RISK. YOUR ACCOUNT MIGHT GET FLAGGED/SUSPENDED. I BEAR NO RESPONSIBILITY | |
*/ | |
async function deleteSpamTweets() { | |
// --- CONFIGURATION --- | |
// Set the cutoff date and time. Tweets posted AFTER this time will be deleted. | |
// Format: YYYY-MM-DDTHH:MM:SS | |
const CUTOFF_DATE = new Date('2025-06-27T05:00:00'); | |
// Set a random delay between actions to avoid rate limits. | |
// Values are in milliseconds (e.g., 5000 = 5 seconds). | |
const MIN_DELAY_MS = 5000; // 5 seconds | |
const MAX_DELAY_MS = 10000; // 10 seconds | |
// --- SCRIPT LOGIC (No need to edit below this line) --- | |
console.log(`Starting tweet deletion process.`); | |
console.log(`Will delete tweets posted after: ${CUTOFF_DATE.toLocaleString()}`); | |
// Helper function for creating a randomized delay | |
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
const randomDelay = () => Math.floor(Math.random() * (MAX_DELAY_MS - MIN_DELAY_MS + 1)) + MIN_DELAY_MS; | |
let tweetsDeleted = 0; | |
// Main loop to continuously find and delete tweets | |
while (true) { | |
// Find all tweet articles currently visible on the page | |
const articles = Array.from(document.querySelectorAll('article[data-testid="tweet"]')); | |
if (articles.length === 0) { | |
console.log("No more tweets found on the page. Scrolling down to load more..."); | |
window.scrollTo(0, document.body.scrollHeight); | |
await sleep(3000); // Wait for new tweets to load | |
// Check again after scroll; if still no tweets, we might be done. | |
if (document.querySelectorAll('article[data-testid="tweet"]').length === 0) { | |
console.log("Finished: No more tweets could be found."); | |
break; | |
} | |
continue; // Restart the loop to process newly loaded tweets | |
} | |
let tweetsFoundInBatch = 0; | |
for (const article of articles) { | |
// Find the timestamp of the tweet | |
const timeElement = article.querySelector('time'); | |
if (!timeElement || !timeElement.dateTime) { | |
continue; // Skip if tweet has no timestamp | |
} | |
const tweetDate = new Date(timeElement.dateTime); | |
// Check if the tweet is new enough to be deleted | |
if (tweetDate > CUTOFF_DATE) { | |
tweetsFoundInBatch++; | |
console.log(`[TARGET FOUND] Tweet from ${tweetDate.toLocaleString()}. Preparing to delete.`); | |
// 1. Click the "More" button (...) | |
const moreButton = article.querySelector('[data-testid="caret"]'); | |
if (!moreButton) { | |
console.error("Could not find the 'More' button for a target tweet. Skipping."); | |
continue; | |
} | |
moreButton.click(); | |
await sleep(1000); // Wait for the dropdown menu to appear | |
// 2. Click the "Delete" option from the menu | |
const menuItems = document.querySelectorAll('div[role="menuitem"]'); | |
const deleteOption = Array.from(menuItems).find(item => item.textContent.includes('Delete')); | |
if (!deleteOption) { | |
console.error("Could not find the 'Delete' option in the menu. Skipping."); | |
// Try to close the menu by clicking the body | |
document.body.click(); | |
continue; | |
} | |
deleteOption.click(); | |
await sleep(1000); // Wait for the confirmation dialog | |
// 3. Click the final "Delete" confirmation button | |
const confirmButton = document.querySelector('[data-testid="confirmationSheetConfirm"]'); | |
if (!confirmButton) { | |
console.error("Could not find the final confirmation button. Skipping."); | |
document.body.click(); | |
continue; | |
} | |
confirmButton.click(); | |
tweetsDeleted++; | |
console.log(`[SUCCESS] Tweet deleted. Total deleted so far: ${tweetsDeleted}`); | |
// Wait for a random period before processing the next tweet | |
const delay = randomDelay(); | |
console.log(`Waiting for ${delay / 1000} seconds to avoid rate limits...`); | |
await sleep(delay); | |
} else { | |
// This tweet is older than the cutoff, so we assume all subsequent tweets are also old. | |
console.log(`Found a tweet from ${tweetDate.toLocaleString()}, which is before the cutoff. Assuming all older tweets are safe.`); | |
console.log(`Process finished. Total tweets deleted: ${tweetsDeleted}.`); | |
return; // Exit the entire function | |
} | |
} | |
// If we processed a batch but didn't find any tweets to delete, it means we've reached older content. | |
if (tweetsFoundInBatch === 0) { | |
console.log("No more tweets found after the cutoff date. Process complete."); | |
break; | |
} | |
} | |
} | |
// Run the function | |
deleteSpamTweets(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
<3 thanks man