Last active
November 11, 2024 19:15
-
-
Save heycassidy/ee8fc8bac95e5ca6b8bb071723b8ee9c to your computer and use it in GitHub Desktop.
Unlike Tweets
This file contains 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
// // THIS SCRIPT DELETES YOUR LIKES | |
// // I AM NOT RESPONSIBLE IF YOUR ACCOUNT GETS BANNED OR SUSPENDED BECAUSE IDK MAYBE THIS IS AGAINST TWITTER TOS | |
// 1. Go to your likes page, e.g. https://x.com/USERNAME/likes | |
// 2. Open the browser JavaScript console | |
// 3. Paste this script | |
// 4. Hit enter to run | |
// Store the original XMLHttpRequest prototype's open and send methods | |
const originalXHROpen = XMLHttpRequest.prototype.open; | |
const originalXHRSend = XMLHttpRequest.prototype.send; | |
let isRateLimited = false; | |
XMLHttpRequest.prototype.open = function(method, url, ...rest) { | |
this._url = url; // Store the URL for later use | |
return originalXHROpen.call(this, method, url, ...rest); | |
}; | |
XMLHttpRequest.prototype.send = function(...args) { | |
this.addEventListener('load', function() { | |
if (this.status === 429) { | |
console.error(`Rate limit error: 429 Too Many Requests for ${this._url}`); | |
isRateLimited = true; | |
} | |
}); | |
this.addEventListener('error', function() { | |
console.error(`Network error during XHR to ${this._url}`); | |
}); | |
return originalXHRSend.apply(this, args); | |
}; | |
// Unliking script with rate limit handling | |
(async function unlikeAllTweets() { | |
let totalUnliked = 0; | |
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
const delayBetweenUnlikes = 100; // Delay between each unlike (adjust as needed) | |
const rateLimitPause = 90000; // Pause for 1 minute if rate-limited | |
const scrollDelay = 2000; // Time to wait after scrolling to load new tweets | |
const scrollDistance = 2000; // Distance to scroll | |
while (true) { | |
// Check for rate limit | |
if (isRateLimited) { | |
console.log(`Rate limit detected. Pausing for ${rateLimitPause / 1000} seconds...`); | |
await wait(rateLimitPause); | |
isRateLimited = false; | |
console.log("Resuming unliking process..."); | |
} | |
// Find all unlike buttons on the page | |
const likeButtons = document.querySelectorAll('button[data-testid="unlike"]'); | |
if (likeButtons.length === 0) { | |
console.log("No likes found on the current view, scrolling to load more..."); | |
window.scrollBy(0, scrollDistance); | |
await wait(scrollDelay); // Wait for new tweets to load | |
continue; | |
} | |
for (let i = 0; i < likeButtons.length; i++) { | |
if (isRateLimited) break; // Stop unliking if rate-limited | |
try { | |
// Get the tweet text associated with the unlike button | |
const tweetElement = likeButtons[i].closest('article'); | |
const tweetText = tweetElement | |
? Array.from(tweetElement.querySelectorAll('[data-testid="tweetText"]')) | |
.map(el => el.innerText) | |
.join(" ") | |
: "No text found"; | |
// Click the unlike button | |
console.log(`Unliking tweet #${totalUnliked + 1}: "${tweetText}"`); | |
likeButtons[i].click(); | |
totalUnliked++; | |
console.log(`Successfully unliked tweet #${totalUnliked}: "${tweetText}"`); | |
// Wait a bit between each unlike to avoid rate limits | |
await wait(delayBetweenUnlikes); | |
} catch (error) { | |
console.error("Error unliking tweet:", error); | |
} | |
} | |
// Scroll down after unliking all visible likes | |
window.scrollBy(0, scrollDistance); | |
await wait(scrollDelay); // Allow time for new tweets to load | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment