-
-
Save Throwaway-MM/5a8e74895293eae0071cec612477c72f to your computer and use it in GitHub Desktop.
// This is a hack, a quick and dirty console script for RT/tweets (with replies) removal w/o API | |
// To be used in: https://twitter.com/Username/with_replies | |
// Set your username (without @) below (case-sensitive) to correctly trigger the right Menu | |
const tweetUser = 'Username' | |
// BUG, With above we still trigger Menu on some replies but relatively harmless. | |
// @Hack Implement simple has() for querySelector | |
const querySelectorHas = function( parent, child ){ | |
return [].filter.call( document.querySelectorAll( parent ), function( elem ){ | |
if(elem.querySelector( child ) !== null ) { | |
return true | |
} | |
else { | |
return false | |
} | |
}); | |
} | |
// @Hack Implement xpath text() selector returning matching holder element | |
// equiv XPath //find[text()='inner'] | |
const querySelectorInner = function( parent, inner ){ | |
return [].filter.call( document.querySelectorAll( parent ), function( elem ){ | |
if(elem.innerHTML == inner ) { | |
return true | |
} | |
else { | |
return false | |
} | |
}); | |
} | |
setInterval(() => { | |
console.log('--- Twitter Removal Clicks Round') | |
// For Old RT's may need (?) to RT and then Unretweet - Just watch for the UI rate limits | |
var unretweets = 0 | |
for (const d of document.querySelectorAll('div[data-testid="unretweet"]')) { | |
unretweets++ | |
d.click() | |
} | |
var unretweetconfirms = 0 | |
for (const r of document.querySelectorAll('div[data-testid="unretweetConfirm"]')) { | |
unretweetconfirms++ | |
r.click() | |
} | |
console.log('Unretweets: ' + unretweets + ', Confirms: ' + unretweetconfirms) | |
var clicks = 0 | |
for(const d of querySelectorHas("div[data-testid='tweet']", "a[href='/" + tweetUser + "']")) { | |
const moreButton = d.querySelector("div[aria-label='More']") | |
clicks++ | |
moreButton.click() | |
} | |
var clickDeletes = 0 | |
for(const deleteButton of querySelectorInner("span", 'Delete')) { | |
deleteButton.click() | |
clickDeletes++ | |
} | |
var clickConfirms = 0 | |
for(const deleteConfirm of document.querySelectorAll("div[data-testid='confirmationSheetConfirm']")) { | |
deleteConfirm.click() | |
clickConfirms++ | |
} | |
console.log('Menu: ' + clicks + ', Deletes: ' + clickDeletes + ', Confirms: ' + clickConfirms) | |
// Scrolling is more involved/difficult as twitter does not hide rest of the thread if reply deleted | |
// As work-around scroll yourself when removals hit zero. API would be best option but this solution is WEB UI driven. | |
window.scrollTo(0, document.body.scrollHeight) | |
}, 3000) // Run every 3s | |
/******************************************************************************************* | |
* | |
* THIS GIST IS PROVIDED "AS IS" AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
*******************************************************************************************/ |
This worked brilliantly and was exactly what I was looking for. Bravo, ma'am. Yes, I noticed that occasional scroll-up is necessary, but that is a far, far better situation than going one by one. This is nicely executed. I appreciate it, and will try your like script next! CHEERS!
@KDReeves Thanks and much appreciated for your kind words :) 💁
const tweetUser = window.location.pathname.match(/^\/([^/]+)/)[1]
I love the script, I tested it and it works great!
But could you also make a script that only unretweet retweets. This scripts deletes all tweets as well.
Thanks for your reply!
I too would love to see a working version that only removes retweets, not tweets.
My "Tweets & replies" is empty but "Tweets" tab is full of retweets, and they don't get deleted.
Also there's no option manually to Undo Retweet, and if I Retweet again then Undo Retweet it seems to get removed but after refreshing the page they come back again.
how use this
Today, This code only un retweet working. But delete tweets not working
You haven't retweet under code running for delete tweets.
setInterval(() => {
for (const d of document.querySelectorAll('div[data-testid="caret"]')) {
d.click()
document.querySelector('div[role="menu"] div[role="menuitem"]').click()
document.querySelector('div[role="alertdialog"] div[data-testid="confirmationSheetConfirm"]').click()
}
window.scrollTo(0, document.body.scrollHeight)
}, 1000)
For those asking for unretweet only then you just need to keep only the top portion of the setInterval loop. Modified code below. The other code is for deleting tweets.
setInterval(() => {
console.log('--- Twitter Removal Clicks Round')
// For Old RT's may need (?) to RT and then Unretweet - Just watch for the UI rate limits
var unretweets = 0
for (const d of document.querySelectorAll('div[data-testid="unretweet"]')) {
unretweets++
d.click()
}
var unretweetconfirms = 0
for (const r of document.querySelectorAll('div[data-testid="unretweetConfirm"]')) {
unretweetconfirms++
r.click()
}
console.log('Unretweets: ' + unretweets + ', Confirms: ' + unretweetconfirms)
}, 3000) // Run every 3s
Thanks original author for the base code. Love it! Only code snippet I found for the new UI.
i'm sorry, i dont get it, im confused using this. can you tell step by step to unretweet?
Steps for only unretweeting-
- Open your profile page on twitter in Chrome Desktop.(url will be like - https://twitter.com/ "Your profile name")
- Select the "tweets" tab
- Press F12 to open Developers Console.
- Select the "Console" Tab
- Paste the script snippet by @Tilduke (for only deleting your retweets and not your own tweets) and Press "Enter".
- You may also be needed to scroll down to load your retweets occasionally.
it worked, thanks so much!
Thanks for the script! It only seemed to only work for retweets for me though. To get it to work for tweets I hacked it yet further. I got rid of the querySelectorHas
and just matched the "More" button on each tweet directly. This has the side-effect of opening up other menus on the page with "More" such as trends, but these don't open a menu with a "Delete" item so nothing bad happens. Note this only deletes tweets now, not retweets, so run the original code to get them too. It also doesn't run with a delay between deletes, so you eventually get rate limited. For a few hundred tweets this is fine but if you've got thousands you might want to adapt the script to add a delay (my JavaScript knowledge is not good enough and I'm too lazy to look it up since it worked well enough for me already).
// This is a hack, a quick and dirty console script for RT/tweets (with replies) removal w/o API
// To be used in: https://twitter.com/Username/with_replies
// Set your username (without @) below (case-sensitive) to correctly trigger the right Menu
const tweetUser = 'Username'
// BUG, With above we still trigger Menu on some replies but relatively harmless.
// @Hack Implement xpath text() selector returning matching holder element
// equiv XPath //find[text()='inner']
const querySelectorInner = function( parent, inner ){
return [].filter.call( document.querySelectorAll( parent ), function( elem ){
if(elem.innerHTML == inner ) {
return true
}
else {
return false
}
});
}
setInterval(() => {
console.log('--- Twitter Removal Clicks Round')
var clicks = 0
for(const d of document.querySelectorAll("div[data-testid='tweet'], div[aria-label='More']")) {
clicks++
d.click()
}
var clickDeletes = 0
for(const deleteButton of querySelectorInner("span", 'Delete')) {
deleteButton.click()
clickDeletes++
}
var clickConfirms = 0
for(const deleteConfirm of document.querySelectorAll("div[data-testid='confirmationSheetConfirm']")) {
deleteConfirm.click()
clickConfirms++
}
console.log('Menu: ' + clicks + ', Deletes: ' + clickDeletes + ', Confirms: ' + clickConfirms)
// Scrolling is more involved/difficult as twitter does not hide rest of the thread if reply deleted
// As work-around scroll yourself when removals hit zero. API would be best option but this solution is WEB UI driven.
window.scrollTo(0, document.body.scrollHeight)
}, 3000) // Run every 3s
/*******************************************************************************************
*
* THIS GIST IS PROVIDED "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************************/
Guys, it needed a fresh update for 2023+:
// BUG, With above we still trigger Menu on some replies but relatively harmless.
// @Hack Implement xpath text() selector returning matching holder element
// equiv XPath //find[text()='inner']
const querySelectorInner = function( parent, inner ){
return [].filter.call( document.querySelectorAll( parent ), function( elem ){
if(elem.innerHTML == inner ) {
return true
}
else {
return false
}
});
}
setInterval(() => {
console.log('--- Twitter Removal Clicks Round')
var clicks = 0
for(const d of document.querySelectorAll("div[data-testid='tweet'], div[aria-label='Plus']")) {
clicks++
d.click()
}
var clickDeletes = 0
for(const deleteButton of querySelectorInner("span", 'Supprimer')) {
deleteButton.click()
clickDeletes++
}
var clickConfirms = 0
for(const deleteConfirm of document.querySelectorAll("div[data-testid='confirmationSheetDialog']")) {
for(const deleteButton2 of querySelectorInner("span", 'Supprimer')) {
deleteButton2.click()
clickConfirms++
}
}
console.log('Menu: ' + clicks + ', Deletes: ' + clickDeletes + ', Confirms: ' + clickConfirms)
// Scrolling is more involved/difficult as twitter does not hide rest of the thread if reply deleted
// As work-around scroll yourself when removals hit zero. API would be best option but this solution is WEB UI driven.
window.scrollTo(0, document.body.scrollHeight)
}, 3000);
If you also want to delete Likes see:
https://gist.github.com/aymericbeaumet/d1d6799a1b765c3c8bc0b675b1a1547d
Deleting RT, replies and tweets is more involved on twitter REACT UI wise as there is
Somebody wrote one earlier for this but it,
a) didn't delete multiple RT/tweets per interval run
b) was hard coded reliant on looking DOM div elements by ever-changing "obfuscated" classes strings therefore matching wrong things
c) broke execution chain (infinite recursion) if there was one unmatched element at any stage
This gist finds the correct menu elements (set your username so it finds your own tweets), runs from interval timer and catch-all-actions from window scroll point.
BUGS
i) Deleting replies is trickier since twitter UI leaves the underlying thread lying around so scrolling is not accurate - just scroll back up manually to hit some more.
ii) Manual scrolling back up may be required while interval function runs
iii) Does not detect twitter rate limit for deletions so you may need to re-run and maybe re-tweet and unretweet (?)
To stop the script just close the page as it runs in your console.
Yes I should have used XPath instead of two filter funcs but didn't think straight tonight 🙄
Also I left logging there to see what's going on and note it is Web2.0 thingy so even if twitter UI shows deleted it may not be the truth.