Skip to content

Instantly share code, notes, and snippets.

@Throwaway-MM
Last active July 2, 2024 11:11
Show Gist options
  • Save Throwaway-MM/5a8e74895293eae0071cec612477c72f to your computer and use it in GitHub Desktop.
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.
*
*******************************************************************************************/
@SeanDS
Copy link

SeanDS commented Nov 26, 2021

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.
 * 
 *******************************************************************************************/

@timenough
Copy link

timenough commented Feb 9, 2023

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);

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