💔 Unlike Old Tweets - XActions

Content Tools scripts/twitter
v1.0.0 Updated: 2026-01-26 246 lines by @nichxbt

Unlikes tweets older than a specified number of days from your likes page

How to Use

  1. Navigate to x.com and log in
  2. Open DevTools Console (F12 or Cmd+Option+I)
  3. Paste the script below and press Enter

Configuration Options

OptionDefaultDescription
daysOld30Only unlike tweets older than this many days
maxUnlikes500Maximum number of tweets to unlike (set to Infinity for all)
minDelay1000Minimum delay between unlikes (ms)
maxDelay2500Maximum delay between unlikes (ms)
scrollDelay1500Delay after scrolling to load more tweets (ms)
maxScrollAttempts10Max scroll attempts when no new old tweets found
logProgresstrueLog progress updates

Default Configuration

const CONFIG = {
    daysOld: 30,               // Only unlike tweets older than this many days
    maxUnlikes: 500,           // Maximum number of tweets to unlike (set to Infinity for all)
    minDelay: 1000,            // Minimum delay between unlikes (ms)
    maxDelay: 2500,            // Maximum delay between unlikes (ms)
    scrollDelay: 1500,         // Delay after scrolling to load more tweets (ms)
    maxScrollAttempts: 10,     // Max scroll attempts when no new old tweets found
    logProgress: true          // Log progress updates
  };

Full Script

Copy and paste this entire script into your browser DevTools console on x.com.

/**
 * ============================================================================
 * 📅 Unlike Old Tweets - XActions
 * ============================================================================
 * 
 * @name        unlike-old.js
 * @description Unlikes tweets older than a specified number of days from your likes page
 * @author      nichxbt
 * @version     1.0.0
 * @date        2026-01-26
 * @usage       Go to x.com/YOUR_USERNAME/likes, open console, paste & run
 * 
 * ============================================================================
 */

(async function unlikeOldTweets() {
  'use strict';

  // ═══════════════════════════════════════════════════════════════════════════
  // 🎛️ CONFIGURATION - Customize these settings
  // ═══════════════════════════════════════════════════════════════════════════
  const CONFIG = {
    daysOld: 30,               // Only unlike tweets older than this many days
    maxUnlikes: 500,           // Maximum number of tweets to unlike (set to Infinity for all)
    minDelay: 1000,            // Minimum delay between unlikes (ms)
    maxDelay: 2500,            // Maximum delay between unlikes (ms)
    scrollDelay: 1500,         // Delay after scrolling to load more tweets (ms)
    maxScrollAttempts: 10,     // Max scroll attempts when no new old tweets found
    logProgress: true          // Log progress updates
  };

  // ═══════════════════════════════════════════════════════════════════════════
  // 🎯 SELECTORS
  // ═══════════════════════════════════════════════════════════════════════════
  const SELECTORS = {
    unlikeButton: '[data-testid="unlike"]',
    tweet: 'article[data-testid="tweet"]',
    tweetText: '[data-testid="tweetText"]',
    timestamp: 'time[datetime]'
  };

  // ═══════════════════════════════════════════════════════════════════════════
  // 🛠️ HELPERS
  // ═══════════════════════════════════════════════════════════════════════════
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
  
  const randomDelay = () => {
    return Math.floor(Math.random() * (CONFIG.maxDelay - CONFIG.minDelay + 1)) + CONFIG.minDelay;
  };

  const formatTime = (seconds) => {
    if (seconds < 60) return `${seconds}s`;
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins}m ${secs}s`;
  };

  const scrollToBottom = () => {
    window.scrollTo({
      top: document.body.scrollHeight,
      behavior: 'smooth'
    });
  };

  const getTweetAge = (tweetElement) => {
    const timeElement = tweetElement.querySelector(SELECTORS.timestamp);
    if (!timeElement) return null;
    
    const tweetDate = new Date(timeElement.getAttribute('datetime'));
    const now = new Date();
    const diffMs = now - tweetDate;
    const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
    
    return {
      days: diffDays,
      date: tweetDate,
      isOld: diffDays >= CONFIG.daysOld
    };
  };

  const findOldTweetsWithUnlike = () => {
    const tweets = document.querySelectorAll(SELECTORS.tweet);
    const oldTweets = [];
    
    tweets.forEach(tweet => {
      const unlikeButton = tweet.querySelector(SELECTORS.unlikeButton);
      if (!unlikeButton) return;
      
      const age = getTweetAge(tweet);
      if (age && age.isOld) {
        oldTweets.push({
          element: tweet,
          button: unlikeButton,
          age: age
        });
      }
    });
    
    return oldTweets;
  };

  // ═══════════════════════════════════════════════════════════════════════════
  // 🚀 MAIN SCRIPT
  // ═══════════════════════════════════════════════════════════════════════════
  
  console.clear();
  console.log(`
╔══════════════════════════════════════════════════════════════╗
║  📅 UNLIKE OLD TWEETS - XActions                             ║
║  by nichxbt                                                  ║
╚══════════════════════════════════════════════════════════════╝
  `);

  // Verify we're on the likes page
  if (!window.location.href.includes('/likes')) {
    console.error('❌ Error: Please navigate to your likes page first!');
    console.log('📍 Go to: x.com/YOUR_USERNAME/likes');
    return;
  }

  const cutoffDate = new Date();
  cutoffDate.setDate(cutoffDate.getDate() - CONFIG.daysOld);

  console.log('📋 Configuration:');
  console.log(`   • Unlike tweets older than: ${CONFIG.daysOld} days`);
  console.log(`   • Cutoff date: ${cutoffDate.toLocaleDateString()}`);
  console.log(`   • Max unlikes: ${CONFIG.maxUnlikes === Infinity ? 'Unlimited' : CONFIG.maxUnlikes}`);
  console.log(`   • Delay range: ${CONFIG.minDelay}ms - ${CONFIG.maxDelay}ms`);
  console.log('');

  console.log('🚀 Starting unlike process for old tweets...');
  console.log('');

  // Stats tracking
  const stats = {
    unliked: 0,
    skipped: 0,
    errors: 0,
    startTime: Date.now(),
    processedTweets: new Set()
  };

  let scrollAttempts = 0;
  let consecutiveNoOldTweets = 0;

  // Main loop
  while (stats.unliked < CONFIG.maxUnlikes) {
    // Find old tweets with unlike buttons
    const oldTweets = findOldTweetsWithUnlike();
    
    // Filter out already processed tweets
    const newOldTweets = oldTweets.filter(t => {
      const tweetId = t.element.getAttribute('aria-labelledby') || 
                      t.element.textContent.substring(0, 50);
      return !stats.processedTweets.has(tweetId);
    });

    if (newOldTweets.length === 0) {
      consecutiveNoOldTweets++;
      
      // Scroll to load more tweets
      if (scrollAttempts >= CONFIG.maxScrollAttempts) {
        console.log('📭 No more old tweets found after scrolling.');
        break;
      }
      
      // Check if we've been scrolling with no results
      if (consecutiveNoOldTweets >= 3) {
        console.log(`📜 Scrolling to find older tweets... (attempt ${scrollAttempts + 1}/${CONFIG.maxScrollAttempts})`);
      }
      
      scrollToBottom();
      await sleep(CONFIG.scrollDelay);
      scrollAttempts++;
      continue;
    }

    // Reset counters when we find old tweets
    scrollAttempts = 0;
    consecutiveNoOldTweets = 0;

    // Process the first old tweet
    const { element, button, age } = newOldTweets[0];
    const tweetId = element.getAttribute('aria-labelledby') || 
                    element.textContent.substring(0, 50);
    
    stats.processedTweets.add(tweetId);

    try {
      // Scroll tweet into view
      element.scrollIntoView({ behavior: 'smooth', block: 'center' });
      await sleep(400);
      
      // Click the unlike button
      button.click();
      stats.unliked++;
      
      // Log progress with age info
      if (CONFIG.logProgress && stats.unliked % 5 === 0) {
        const elapsed = Math.floor((Date.now() - stats.startTime) / 1000);
        console.log(`📅 Progress: ${stats.unliked} unliked | ⏱️ ${formatTime(elapsed)} elapsed`);
      } else {
        console.log(`💔 Unliked tweet from ${age.days} days ago (${age.date.toLocaleDateString()})`);
      }
      
      // Random delay before next action
      await sleep(randomDelay());
      
    } catch (error) {
      stats.errors++;
      console.warn(`⚠️ Error unliking tweet: ${error.message}`);
      await sleep(1000);
    }

    // Check if we've hit the limit
    if (stats.unliked >= CONFIG.maxUnlikes) {
      console.log(`🎯 Reached maximum unlike limit (${CONFIG.maxUnlikes})`);
      break;
    }
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // 📊 COMPLETION SUMMARY
  // ═══════════════════════════════════════════════════════════════════════════
  const totalTime = Math.floor((Date.now() - stats.startTime) / 1000);
  const avgTime = stats.unliked > 0 ? (totalTime / stats.unliked).toFixed(2) : 0;

  console.log('');
  console.log(`
╔══════════════════════════════════════════════════════════════╗
║  ✅ UNLIKE OLD TWEETS COMPLETE                               ║
╠══════════════════════════════════════════════════════════════╣
║  📋 Criteria: Tweets older than ${String(CONFIG.daysOld + ' days').padEnd(25)}║
║  📊 Results:                                                 ║
║     📅 Old tweets unliked: ${String(stats.unliked).padEnd(33)}║
║     ❌ Errors: ${String(stats.errors).padEnd(45)}║
║     ⏱️  Total time: ${formatTime(totalTime).padEnd(40)}║
║     📈 Avg time/unlike: ${String(avgTime + 's').padEnd(36)}║
╚══════════════════════════════════════════════════════════════╝
  `);

  console.log('👋 Thanks for using XActions! Follow @nichxbt for updates.');
  
  return stats;
})();

⚡ More XActions Scripts

Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.

Browse All Scripts