-
-
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. | |
* | |
*******************************************************************************************/ |
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);
Today, This code only un retweet working. But delete tweets not working
You haven't retweet under code running for delete tweets.