Created
October 13, 2023 13:03
-
-
Save GrzegorzManiak/b2d5b56ee1ecda74f62fec49e7d3efe0 to your computer and use it in GitHub Desktop.
Deletes all your replys on Twitter / X automatically
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ENSURE that you are on your own page eg https://twitter.com/x/with_replies | |
// Wait for the page to fully load, and than copy / paste this script | |
// into the console. | |
// | |
// There are two variables that you can change, SLEEP_FOR and CLICK_DELAY | |
// --> SLEEP_FOR: Min,Max timeout before scrolling down | |
// --> CLICK_DELAY: Min,Max timeout between clicking the element | |
// | |
(async() => { | |
const SLEEP_FOR = [1000, 2000]; | |
const CLICK_DELAY = [350, 1000]; | |
// -- Map to store the reply content | |
const posts_map = new Map(); | |
// -- Dir='ltr' exits on some elements, we just need to find the | |
// one where theres 'posts' in the innerText | |
const elms = Array.from(document.querySelectorAll('[dir="ltr"]')), | |
posts = elms.filter((item) => item.innerText.includes('posts')); | |
// -- Skip the first element and get the second one | |
const posts_elm = posts[1]; | |
if (!posts_elm) throw new Error('Could not find the posts element'); | |
// -- We'll get the users name and tag from the page title as its | |
// easier than messing around with the DOM | |
// FORMAT: Page title: Posts with replies by x (@x) / X | |
const page_title = document.querySelector('title').innerText, | |
regex = /by (.+) \(@(.+)\)/; | |
// -- Get the users name and tag | |
const user_name = page_title.match(regex)[1], | |
user_tag = page_title.match(regex)[2]; | |
console.log(`Deleting replys for ${user_name} (@${user_tag})`); | |
// -- Function used to return the total number of posts youve replied to | |
const get_total_posts = () => { | |
const total_posts = parseInt(posts_elm.innerText.split(' ')[0].replace(',', '')); | |
console.log(`Total replys remaining: ${total_posts}`); | |
return total_posts; | |
}; | |
// -- Function used to return all the reply content on the page | |
// without returning duplicates | |
const get_replied = () => { | |
// -- Get all the replys on the page | |
const replied_raw = Array.from(document.querySelectorAll('article[data-testid="tweet"][role="article"]')), | |
replied = replied_raw.filter((item) => { | |
const name_elm = item.querySelector('[data-testid="User-Name"]'); | |
if (!name_elm) return false; | |
// -- Ensure that the reply has a delete button, eg. its your reply | |
if ( | |
name_elm.innerHTML.includes(user_name) && | |
name_elm.innerHTML.includes(user_tag) | |
) return true; | |
// -- Not your reply | |
return false; | |
}); | |
// -- Ensure that we arent returning duplicates | |
const unique = replied.filter((item) => !posts_map.has(item)); | |
// -- Add the reply content to the map | |
unique.forEach((item) => posts_map.set(item, false)); | |
// -- Return the unique reply content | |
return unique; | |
}; | |
// -- Simple sleep function | |
const sleep = (min_max) => new Promise((resolve) => { | |
const min = min_max[0], max = min_max[1]; | |
const time = Math.floor(Math.random() * (max - min + 1)) + min; | |
setTimeout(resolve, time); | |
}); | |
// -- Progresses down the page and deletes all the content | |
const step = async() => { | |
// -- Get the liked content | |
const replied = get_replied(); | |
// -- Unlikes all the content | |
for (const item of replied) { | |
// -- Click the '...' button: aria-label="More" | |
const more_btn = item.querySelector('[aria-label="More"]'); | |
if (!more_btn) continue; | |
// -- Click the more button | |
more_btn.click(); | |
await sleep(CLICK_DELAY); | |
// -- Click the 'Delete' button: first child of data-testid="Dropdown" | |
const delete_btn = document.querySelector('[data-testid="Dropdown"] > div'); | |
if (!delete_btn) continue; | |
// -- Click the delete button | |
delete_btn.click(); | |
await sleep(CLICK_DELAY); | |
// -- Popup confirmation: h1[role="heading"] | |
const confirm = document.querySelector('div[data-testid="confirmationSheetDialog"]').querySelector('[role="button"]') | |
if (!confirm) continue; | |
// -- Click the confirm button | |
confirm.click(); | |
await sleep(CLICK_DELAY); | |
} | |
// -- Scrolls down the page | |
window.scrollTo(0, document.body.scrollHeight); | |
} | |
const started_with = get_total_posts(); | |
// -- Run the step function every 2 seconds | |
while (true) { | |
await step(); | |
await sleep(SLEEP_FOR); | |
// -- If there are no more post left, exit the loop | |
if (get_total_posts() === 0) break; | |
} | |
console.log(`Delted ${started_with} posts`); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment