๐Ÿ“ก Monitoring Tutorials

๐ŸŽ‰ XActions is 100% Free & Open Source Star on GitHub โญ

๐Ÿ”ญ Monitor Any Account

Track follows and unfollows on any public X account over time. Great for competitor analysis or tracking influential accounts.

Step 1: Go to any account's followers or following page

Navigate to x.com/USERNAME/followers or x.com/USERNAME/following

Step 2: Paste the Monitor Account script

monitorAccount.js
// Monitor Any Account - by nichxbt
// https://github.com/nirholas/XActions
// Track follows/unfollows on ANY public X account
// Go to x.com/USERNAME/followers or /following

(() => {
  const STORAGE_PREFIX = 'xactions_monitor_';
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  const path = window.location.pathname;
  const isFollowers = path.includes('/followers');
  const isFollowing = path.includes('/following');

  if (!isFollowers && !isFollowing) {
    console.error('โŒ Please go to a followers or following page!');
    console.log('Example: x.com/elonmusk/followers');
    return;
  }

  const pageType = isFollowers ? 'followers' : 'following';
  const targetUser = path.split('/')[1].toLowerCase();
  const storageKey = `${STORAGE_PREFIX}${targetUser}_${pageType}`;

  console.log(`๐Ÿ”ญ Monitoring @${targetUser}'s ${pageType}...\n`);

  const scrapeUsers = async () => {
    const users = new Set();
    let prevSize = 0;
    let retries = 0;

    while (retries < 5) {
      window.scrollTo(0, document.body.scrollHeight);
      await sleep(1500);

      document.querySelectorAll('[data-testid="UserCell"]').forEach(cell => {
        const links = cell.querySelectorAll('a[href^="/"]');
        links.forEach(link => {
          const href = link.getAttribute('href');
          if (href?.startsWith('/') && !href.includes('/status/')) {
            const username = href.replace('/', '').split('/')[0].toLowerCase();
            if (username && username !== targetUser && !username.includes('?')) {
              users.add(username);
            }
          }
        });
      });

      console.log(`Found ${users.size} accounts...`);
      if (users.size === prevSize) retries++;
      else { retries = 0; prevSize = users.size; }
    }

    return Array.from(users);
  };

  const loadPrevious = () => {
    try {
      const data = localStorage.getItem(storageKey);
      return data ? JSON.parse(data) : null;
    } catch { return null; }
  };

  const saveData = (users) => {
    const data = {
      target: targetUser,
      type: pageType,
      users,
      timestamp: new Date().toISOString(),
      count: users.length
    };
    localStorage.setItem(storageKey, JSON.stringify(data));
    return data;
  };

  const compare = (previous, current) => {
    const prevSet = new Set(previous);
    const currSet = new Set(current);
    return {
      removed: previous.filter(u => !currSet.has(u)),
      added: current.filter(u => !prevSet.has(u))
    };
  };

  const downloadList = (list, filename) => {
    if (list.length === 0) return;
    const blob = new Blob([list.map(u => `@${u}`).join('\n')], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  scrapeUsers().then(currentUsers => {
    console.log(`\nโœ… Found ${currentUsers.length} accounts\n`);

    const previous = loadPrevious();

    if (previous) {
      const prevDate = new Date(previous.timestamp).toLocaleString();
      console.log(`๐Ÿ“Š Comparing with snapshot from ${prevDate}`);
      console.log(`Previous: ${previous.count} | Current: ${currentUsers.length}\n`);

      const { removed, added } = compare(previous.users, currentUsers);

      if (pageType === 'followers') {
        if (removed.length > 0) {
          console.log(`๐Ÿšจ ${removed.length} UNFOLLOWED @${targetUser}:`);
          removed.forEach(u => console.log(`   @${u}`));
          downloadList(removed, `${targetUser}-lost-followers.txt`);
        }
        if (added.length > 0) {
          console.log(`\n๐ŸŽ‰ ${added.length} NEW FOLLOWERS:`);
          added.forEach(u => console.log(`   @${u}`));
        }
      } else {
        if (removed.length > 0) {
          console.log(`๐Ÿ‘‹ @${targetUser} UNFOLLOWED ${removed.length}:`);
          removed.forEach(u => console.log(`   @${u}`));
        }
        if (added.length > 0) {
          console.log(`\nโž• @${targetUser} NOW FOLLOWS ${added.length} NEW:`);
          added.forEach(u => console.log(`   @${u}`));
        }
      }

      if (removed.length === 0 && added.length === 0) {
        console.log('โœจ No changes since last check!');
      }
    } else {
      console.log('๐Ÿ“ธ First scan! Saving snapshot...');
      console.log('Run again later to see changes.');
    }

    saveData(currentUsers);
    console.log(`\n๐Ÿ’พ Snapshot saved. Run again anytime!\n`);
  });
})();

๐Ÿ’ก How It Works

The script saves a snapshot of the account's followers/following list in your browser's localStorage. When you run it again, it compares with the previous snapshot to show who was added or removed.

๐ŸŽ‰ New Follower Alerts

Track your new followers with display names and get ready-to-use welcome message templates.

Step 1: Go to your followers page

Navigate to x.com/YOUR_USERNAME/followers

Step 2: Paste the New Followers Alert script

newFollowersAlert.js
// New Followers Alert - by nichxbt
// https://github.com/nirholas/XActions
// Track new followers + get welcome templates
// Go to x.com/YOUR_USERNAME/followers

(() => {
  const STORAGE_KEY = 'xactions_new_followers';
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  if (!window.location.pathname.includes('/followers')) {
    console.error('โŒ Please go to your FOLLOWERS page!');
    return;
  }

  const username = window.location.pathname.split('/')[1];
  console.log(`๐ŸŽ‰ Tracking new followers for @${username}...\n`);

  const scrapeFollowers = async () => {
    const followers = new Map();
    let prevSize = 0;
    let retries = 0;

    while (retries < 5) {
      window.scrollTo(0, document.body.scrollHeight);
      await sleep(1500);

      document.querySelectorAll('[data-testid="UserCell"]').forEach(cell => {
        const link = cell.querySelector('a[href^="/"]');
        const nameEl = cell.querySelector('[dir="ltr"] > span');
        
        if (link) {
          const user = link.getAttribute('href').replace('/', '').split('/')[0].toLowerCase();
          const displayName = nameEl?.textContent || user;
          if (user && user !== username.toLowerCase()) {
            followers.set(user, displayName);
          }
        }
      });

      if (followers.size === prevSize) retries++;
      else { retries = 0; prevSize = followers.size; }
      console.log(`Found ${followers.size} followers...`);
    }

    return followers;
  };

  const loadPrevious = () => {
    try {
      const data = localStorage.getItem(STORAGE_KEY);
      return data ? JSON.parse(data) : null;
    } catch { return null; }
  };

  const saveFollowers = (followersMap) => {
    const data = {
      username,
      followers: Object.fromEntries(followersMap),
      timestamp: new Date().toISOString(),
      count: followersMap.size
    };
    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
    return data;
  };

  scrapeFollowers().then(currentFollowers => {
    console.log(`\nโœ… Total followers: ${currentFollowers.size}\n`);

    const previous = loadPrevious();

    if (previous && previous.username.toLowerCase() === username.toLowerCase()) {
      const prevUsers = new Set(Object.keys(previous.followers));
      const newFollowers = [];

      currentFollowers.forEach((displayName, user) => {
        if (!prevUsers.has(user)) {
          newFollowers.push({ user, displayName });
        }
      });

      const prevDate = new Date(previous.timestamp).toLocaleString();
      console.log(`๐Ÿ“Š Comparing with ${prevDate}`);
      console.log(`Previous: ${previous.count} | Current: ${currentFollowers.size}`);
      console.log(`Net change: ${currentFollowers.size - previous.count > 0 ? '+' : ''}${currentFollowers.size - previous.count}\n`);

      if (newFollowers.length > 0) {
        console.log(`๐ŸŽ‰ ${newFollowers.length} NEW FOLLOWERS:\n`);
        newFollowers.forEach((f, i) => {
          console.log(`${i + 1}. ${f.displayName} (@${f.user})`);
          console.log(`   https://x.com/${f.user}`);
        });

        console.log('\n๐Ÿ“ Welcome templates (copy & customize):');
        console.log('โ”€'.repeat(50));
        newFollowers.slice(0, 5).forEach(f => {
          console.log(`Hey @${f.user}, thanks for the follow! ๐Ÿ™`);
        });
        console.log('โ”€'.repeat(50));
      } else {
        console.log('๐Ÿ“ญ No new followers since last check.');
      }

      // Check for unfollowers
      const currentUsers = new Set(currentFollowers.keys());
      const lost = Object.keys(previous.followers).filter(u => !currentUsers.has(u));
      if (lost.length > 0) {
        console.log(`\n๐Ÿ‘‹ ${lost.length} unfollowed you:`);
        lost.forEach(u => console.log(`   @${u}`));
      }
    } else {
      console.log('๐Ÿ“ธ First scan! Saving your followers...');
      console.log('Run again later to see new followers!');
    }

    saveFollowers(currentFollowers);
    console.log('\n๐Ÿ’พ Snapshot saved!\n');
  });
})();

๐Ÿšจ Detect Unfollowers

Find out exactly who unfollowed you since your last check. Downloads a list of unfollowers.

Step 1: Go to your followers page

Navigate to x.com/YOUR_USERNAME/followers

Step 2: Paste the Detect Unfollowers script

detectUnfollowers.js
// Detect Unfollowers - by nichxbt
// https://github.com/nirholas/XActions
// Find who unfollowed you
// Go to x.com/YOUR_USERNAME/followers

(() => {
  const STORAGE_KEY = 'xactions_my_followers';
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  if (!window.location.pathname.includes('/followers')) {
    console.error('โŒ Please go to your FOLLOWERS page!');
    console.log('๐Ÿ‘‰ Go to: x.com/YOUR_USERNAME/followers');
    return;
  }

  const username = window.location.pathname.match(/^\/([^/]+)\/followers/)?.[1] || 'unknown';
  console.log(`๐Ÿ” Detecting unfollowers for @${username}...\n`);

  const scrapeFollowers = async () => {
    const followers = new Set();
    let prevSize = 0;
    let retries = 0;

    while (retries < 5) {
      window.scrollTo(0, document.body.scrollHeight);
      await sleep(1500);

      document.querySelectorAll('[data-testid="UserCell"]').forEach(cell => {
        const link = cell.querySelector('a[href^="/"]');
        if (link) {
          const href = link.getAttribute('href');
          const user = href.replace('/', '').toLowerCase();
          if (user && !user.includes('/')) {
            followers.add(user);
          }
        }
      });

      console.log(`Found ${followers.size} followers...`);
      if (followers.size === prevSize) retries++;
      else { retries = 0; prevSize = followers.size; }
    }

    return Array.from(followers);
  };

  const loadPrevious = () => {
    try {
      const data = localStorage.getItem(STORAGE_KEY);
      return data ? JSON.parse(data) : null;
    } catch { return null; }
  };

  const saveFollowers = (followers) => {
    const data = {
      username,
      followers,
      timestamp: new Date().toISOString(),
      count: followers.length
    };
    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
    return data;
  };

  scrapeFollowers().then(currentFollowers => {
    console.log(`\nโœ… Found ${currentFollowers.length} followers\n`);

    const previous = loadPrevious();

    if (previous && previous.username === username) {
      const prevDate = new Date(previous.timestamp).toLocaleString();
      console.log(`๐Ÿ“Š Comparing with snapshot from ${prevDate}`);
      console.log(`Previous: ${previous.count} | Current: ${currentFollowers.length}\n`);

      const currSet = new Set(currentFollowers);
      const unfollowed = previous.followers.filter(f => !currSet.has(f.toLowerCase()));
      const newFollowers = currentFollowers.filter(f => !previous.followers.includes(f));

      if (unfollowed.length > 0) {
        console.log(`๐Ÿšจ ${unfollowed.length} PEOPLE UNFOLLOWED YOU:\n`);
        unfollowed.forEach((u, i) => {
          console.log(`${i + 1}. @${u} โ€” https://x.com/${u}`);
        });

        // Download list
        const blob = new Blob([unfollowed.join('\n')], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `unfollowers-${new Date().toISOString().split('T')[0]}.txt`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        console.log('\n๐Ÿ“ฅ Downloaded unfollowers list!');
      } else {
        console.log('โœจ No one unfollowed you since last check!');
      }

      if (newFollowers.length > 0) {
        console.log(`\n๐ŸŽ‰ ${newFollowers.length} NEW FOLLOWERS:`);
        newFollowers.forEach((u, i) => console.log(`${i + 1}. @${u}`));
      }
    } else {
      console.log('๐Ÿ“ธ First scan! Saving snapshot...');
      console.log('Run again later to detect unfollowers.');
    }

    saveFollowers(currentFollowers);
    console.log(`\n๐Ÿ’พ Snapshot saved!\n`);
  });
})();

๐Ÿ’ก Pro Tip

Run this script regularly (daily or weekly) to catch unfollowers quickly. The more frequently you run it, the more accurate your tracking will be.