Skip to content

Instantly share code, notes, and snippets.

@heycassidy
Last active November 11, 2024 19:15
Show Gist options
  • Save heycassidy/ee8fc8bac95e5ca6b8bb071723b8ee9c to your computer and use it in GitHub Desktop.
Save heycassidy/ee8fc8bac95e5ca6b8bb071723b8ee9c to your computer and use it in GitHub Desktop.
Unlike Tweets
// // 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