From e391f03e290400931bf0ee80722fed37c9c59273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Ziad=C3=A9?= Date: Sun, 18 Feb 2024 17:58:06 -0800 Subject: [PATCH] Enhance script to handle infinite scrolling The current script was only deleting whatever was loaded on the first page. This enhancement takes care of scrolling to the bottom to fetch more tweets after each batch delete. I've tested this and it works --- README.md | 105 ++++++++++++++++++++++++++++++++++++------------------ main.js | 58 ++++++++++++++++++------------ 2 files changed, 105 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1a9e139..22f4636 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,83 @@ Script to delete all X / Twitter tweets. (Credits go to ChatGPT, I didn't write any of this code) ### How to run: -- Open Chrome and go to your X profile (https://x.com/USERNAME/with_replies) -- Open Chrome Developer Console. Copy/Paste the code: -``` +- Open Chrome and go to your X profile (https://x.com/USERNAME/with_replies) +- Open Chrome Developer Console. Copy/Paste the code: + +```javascript const deleteAllTweets = async () => { - const processedButtons = new Set(); - const getDeleteButtons = () => Array.from(document.querySelectorAll('[data-testid="tweet"] [data-testid="caret"]')); - const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); - - while (true) { - const deleteButtons = getDeleteButtons().filter(button => !processedButtons.has(button)); - if (deleteButtons.length === 0) break; - - for (const button of deleteButtons) { - processedButtons.add(button); - button.click(); - await delay(250); - - const menuItems = Array.from(document.querySelectorAll('[role="menuitem"]')); - const deleteOption = menuItems.find(item => item.textContent === 'Delete'); - - if (deleteOption) { - deleteOption.click(); - document.querySelector('[data-testid="confirmationSheetConfirm"]')?.click(); - await delay(3000); - } else { - const tweetContainer = button.closest('[data-testid="tweet"]'); - const unretweetButton = tweetContainer?.querySelector('[data-testid="unretweet"]'); - - if (unretweetButton) { - unretweetButton.click(); - await delay(250); - document.querySelector('[data-testid="unretweetConfirm"]')?.click(); - await delay(3000); + const processedButtons = new Set(); + const getDeleteButtons = () => + Array.from( + document.querySelectorAll( + '[data-testid="tweet"] [data-testid="caret"]' + ) + ); + const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + const scrollToEnd = () => window.scrollTo(0, document.body.scrollHeight); + + const attemptDelete = async (button) => { + processedButtons.add(button); + button.click(); + await delay(250 + Math.random() * 250); // Adding some randomness to mimic human behavior + + const menuItems = Array.from( + document.querySelectorAll('[role="menuitem"]') + ); + const deleteOption = menuItems.find((item) => + item.textContent.includes("Delete") + ); + + if (deleteOption) { + deleteOption.click(); + document + .querySelector('[data-testid="confirmationSheetConfirm"]') + ?.click(); + await delay(3000 + Math.random() * 1000); + } else { + const tweetContainer = button.closest('[data-testid="tweet"]'); + const unretweetButton = tweetContainer?.querySelector( + '[data-testid="unretweet"]' + ); + if (unretweetButton) { + unretweetButton.click(); + await delay(250 + Math.random() * 250); + document + .querySelector('[data-testid="unretweetConfirm"]') + ?.click(); + await delay(3000 + Math.random() * 1000); + } + } + }; + + while (true) { + const deleteButtons = getDeleteButtons().filter( + (button) => !processedButtons.has(button) + ); + if (deleteButtons.length === 0) { + scrollToEnd(); + await delay(5000); // Wait for more tweets to load + if ( + getDeleteButtons().filter( + (button) => !processedButtons.has(button) + ).length === 0 + ) { + break; // Exit if no new tweets are loaded + } else { + continue; // Otherwise, continue processing new tweets + } + } + + for (const button of deleteButtons) { + await attemptDelete(button); } - } } - } - console.log('All tweets deleted successfully!'); + console.log("All tweets deleted successfully!"); }; deleteAllTweets(); ``` + Screenshot 2023-12-31 at 7 18 45 PM diff --git a/main.js b/main.js index db17ac5..7ccd1e5 100644 --- a/main.js +++ b/main.js @@ -2,38 +2,50 @@ const deleteAllTweets = async () => { const processedButtons = new Set(); const getDeleteButtons = () => Array.from(document.querySelectorAll('[data-testid="tweet"] [data-testid="caret"]')); const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + const scrollToEnd = () => window.scrollTo(0, document.body.scrollHeight); - while (true) { - const deleteButtons = getDeleteButtons().filter(button => !processedButtons.has(button)); - if (deleteButtons.length === 0) break; + const attemptDelete = async (button) => { + processedButtons.add(button); + button.click(); + await delay(250 + Math.random() * 250); // Adding some randomness to mimic human behavior - for (const button of deleteButtons) { - processedButtons.add(button); - button.click(); - await delay(250); + const menuItems = Array.from(document.querySelectorAll('[role="menuitem"]')); + const deleteOption = menuItems.find(item => item.textContent.includes('Delete')); - const menuItems = Array.from(document.querySelectorAll('[role="menuitem"]')); - const deleteOption = menuItems.find(item => item.textContent === 'Delete'); + if (deleteOption) { + deleteOption.click(); + document.querySelector('[data-testid="confirmationSheetConfirm"]')?.click(); + await delay(3000 + Math.random() * 1000); + } else { + const tweetContainer = button.closest('[data-testid="tweet"]'); + const unretweetButton = tweetContainer?.querySelector('[data-testid="unretweet"]'); + if (unretweetButton) { + unretweetButton.click(); + await delay(250 + Math.random() * 250); + document.querySelector('[data-testid="unretweetConfirm"]')?.click(); + await delay(3000 + Math.random() * 1000); + } + } + }; - if (deleteOption) { - deleteOption.click(); - document.querySelector('[data-testid="confirmationSheetConfirm"]')?.click(); - await delay(3000); + while (true) { + const deleteButtons = getDeleteButtons().filter(button => !processedButtons.has(button)); + if (deleteButtons.length === 0) { + scrollToEnd(); + await delay(5000); // Wait for more tweets to load + if (getDeleteButtons().filter(button => !processedButtons.has(button)).length === 0) { + break; // Exit if no new tweets are loaded } else { - const tweetContainer = button.closest('[data-testid="tweet"]'); - const unretweetButton = tweetContainer?.querySelector('[data-testid="unretweet"]'); - - if (unretweetButton) { - unretweetButton.click(); - await delay(250); - document.querySelector('[data-testid="unretweetConfirm"]')?.click(); - await delay(3000); - } + continue; // Otherwise, continue processing new tweets } } + + for (const button of deleteButtons) { + await attemptDelete(button); + } } console.log('All tweets deleted successfully!'); }; -deleteAllTweets(); +deleteAllTweets(); \ No newline at end of file