Skip to content

Instantly share code, notes, and snippets.

@lucahammer
Last active November 19, 2024 20:42
Show Gist options
  • Save lucahammer/1aa16b4d3c1fb04035839da5ef699d65 to your computer and use it in GitHub Desktop.
Save lucahammer/1aa16b4d3c1fb04035839da5ef699d65 to your computer and use it in GitHub Desktop.
Delete all your Tweets Javascript
/*
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();
@lucahammer
Copy link
Author

Version that works with the tweets.js file from the Twitter Data Export to delete Tweets that aren't visible anymore: https://gist.github.com/lucahammer/a4d1e957ec9e061e3cccafcbed599e16

@franck-paul
Copy link

If the profile language is set to French then replace aria-label="Profile timelines" by aria-label="Fil des profils" on lines 92 and 99, before running this script.

@perigozin
Copy link

Not working anymore :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment