Twitter cleanup

Remove user and topics to follow suggestions from Twitter

  1. // ==UserScript==
  2. // @name Twitter cleanup
  3. // @version 1.9.12
  4. // @grant Sly_North
  5. // @description Remove user and topics to follow suggestions from Twitter
  6. // @author Sly_North
  7. // @match https://x.com/*
  8. // @match https://twitter.com/*
  9. // @match https://mobile.twitter.com/*
  10. // @namespace https://greasyfork.org/en/users/759669-sly-north
  11. // @icon https://pbs.twimg.com/profile_images/1711354492925378560/mWYEK97t_400x400.jpg
  12. // old icon https://abs.twimg.com/responsive-web/client-web/icon-svg.168b89d8.svg
  13. // @license MIT
  14. // @grant none
  15. // ==/UserScript==
  16.  
  17. function RemoveFollowingThinElements(e, removeWithoutFollowButton) {
  18.   console.log('- TWcleanup removing H=', e.getBoundingClientRect().height, ' ', e.innerText);
  19.   let next = e.nextSibling;
  20.   if (next) {
  21.   let nextH = next.getBoundingClientRect().height;
  22.     if (nextH < 200) {
  23.       if (removeWithoutFollowButton || next.innerText.match(/Follow/) || next.innerText.match(/Subscribe/) || next.innerText.match(/Show more/))
  24.         RemoveFollowingThinElements(next, removeWithoutFollowButton);
  25.       else {
  26.         console.log('- TWcleanup stops at H=', nextH, ' "' + next.innerText + '"');
  27.         if (next.innerText === 'Show more') {
  28.           next.innerHTML = "";
  29.         }
  30.       }
  31.     }
  32.   }
  33.   e.innerHTML = "";
  34. }
  35.  
  36. // Tool to remove the "X follows Y" tweets and "See more" suggested topics.
  37. function RemoveSuggestedTweets(name, regex) {
  38.   let elts = Array.from(document.getElementsByTagName('article')).filter(e => e.innerText.match(regex));
  39.   if (elts.length > 0) {
  40.     console.log('- TWcleanup Found ', name, ' count=', elts.length);
  41.     for (let e of elts) {
  42.       console.log('  - TWcleanup remove suggestion: ', e.innerText.substring(0, 40));
  43.       e.innerHTML = "";
  44.     }
  45.   }
  46. }
  47.  
  48. function RemoveSuggestions() {
  49.   // Remove suggested people to follow
  50.   let elts = Array.from(document.getElementsByTagName('H2')).filter(
  51.      // Needs to be in screen for nextSibling to be defined.
  52.      e => e.getBoundingClientRect().top < window.innerHeight &&
  53.            (e.innerText === 'Who to follow') || e.innerText === 'Creators for you');
  54.   if (elts.length > 0) {
  55.     console.log('TWcleanup found "Who to follow"');
  56.     for (let e of elts) {
  57.       e = e.parentElement.parentElement.parentElement.parentElement;
  58.       RemoveFollowingThinElements(e, false);
  59.     }
  60.   }
  61.  
  62.   // Remove suggested topics
  63.   elts = Array.from(document.getElementsByTagName('SPAN')).filter(
  64.     e => // e.getBoundingClientRect().top < window.innerHeight &&
  65.       (e.innerText === 'Topics to follow' || e.innerText === 'Expand your timeline with Topics'));
  66.   if (elts.length > 0) {
  67.     console.log('TWcleanup found "', elts[0].innerText, '"');
  68.     for (let e of elts) {
  69.       e = e.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
  70.       // Remove topics and offset bar
  71.       RemoveFollowingThinElements(e.nextSibling, true);
  72.       // Remove title
  73.       e.innerHTML = "";
  74.     }
  75.     console.log('TWcleanup removed "', title, '"');
  76.   }
  77.  
  78.   const isTwitterHomePage = document.location.href === 'https://x.com/home';
  79.   if (isTwitterHomePage) {
  80.     // Remove "Subscribe" elements
  81.  for (let e of Array.from(document.getElementsByTagName('span')).filter(e => e.innerText == 'Subscribe' && e.getBoundingClientRect().width > 0)) {
  82.       e = e.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
  83.       const height = e.getBoundingClientRect().height;
  84.       if (height > 0 && height < 100) {
  85.         console.log('  - Remove TW Subscribe: H=', height, ' ', e.innerText.substring(0, 40));
  86.         e.innerHTML = ''
  87.       }
  88.     }
  89.  
  90.     // Remove tweets of people followed by one we follow
  91.     RemoveSuggestedTweets('X follows Y', / follows*\n/);
  92.     // Remove "See more" suggestions
  93.     RemoveSuggestedTweets('See more', /\nSee more\n/);
  94. // Remove Premium ads
  95. for (let e of Array.from(document.getElementsByTagName('h1'))
  96. .filter(e => e.innerText.match(/ad.*Premium/))) {
  97. e.parentElement.parentElement.parentElement.innerHTML = "";
  98. }
  99.   }
  100.  
  101.   // Unfreeze scrolling (ie: in incognito after scrolling a bit)
  102.   if (document.documentElement.style.overflow) document.documentElement.style.overflow = "scroll";
  103.  
  104.   let eltCred = document.getElementById('credential_picker_container');
  105.   if (eltCred && eltCred.getBoundingClientRect().width < 400) eltCred.style.display = "none";
  106.  
  107.   // If the window is very small (like when watching a video in a small secondary window),
  108.   // remove the Twitter left column and top banner.
  109.   {
  110.     let elts = document.getElementsByTagName('header');
  111.     if (elts.length > 0) {
  112.       let widthLimit = isTwitterHomePage ? 800 : 900;
  113.       let smallWindow = window.innerWidth < widthLimit;
  114.       elts[0].style.display = smallWindow ? "none" : "";
  115.       let primaryCol = document.querySelector('[data-testid="primaryColumn"]');
  116. if (primaryCol) {
  117. primaryCol.style.maxWidth = smallWindow ? null : '600px';
  118. primaryCol.style.minWidth = smallWindow ? '100vw' : null;
  119. }
  120.     }
  121.     var elt = document.querySelector('[aria-label="Home timeline"]');
  122.     if (elt) elt.firstChild.style.display = (window.innerHeight < 700) ? "none" : "";
  123.   }
  124.  
  125.   setTimeout(RemoveSuggestions, 1000);
  126. }
  127.  
  128. // Remove credential banner (in incognito windows)
  129. let bottomBanners = Array.from(document.getElementsByTagName('div'))
  130.     .filter(e => e.dataset.testid === 'BottomBar' );
  131. if (bottomBanners.length > 0) bottomBanners[0].innerHTML = '';
  132. let elt = document.getElementById('credential_picker_container');
  133. if (elt) elt.innerHTML = "";
  134.  
  135. setTimeout(RemoveSuggestions, 1000);
  136.  
  137. // Change logo to Twitter
  138. const twitterIcon = "https://pbs.twimg.com/profile_images/1711354492925378560/mWYEK97t_400x400.jpg";
  139. setTimeout(() => {
  140.   let logo = Array.from(document.getElementsByTagName('svg')).filter(e => e.getBoundingClientRect().top < 15)[0];
  141.   logo.parentElement.innerHTML = '<img src="' + twitterIcon + '" width=32 height=32>'
  142. }, 500);
  143. // Change favicon
  144. var link = document.querySelector("link[rel~='icon']");
  145. if (!link) {
  146.     link = document.createElement('link');
  147.     link.rel = 'icon';
  148.     document.head.appendChild(link);
  149. }
  150. link.href = twitterIcon;
  151.  
  152.  
  153. /*
  154. // Switch to the Following tab
  155. if (isTwitterHomePage) {
  156.   setTimeout(() => {
  157.   for (let e of document.getElementsByTagName('span')) {
  158.       if (e.innerText === 'Following') { e.click(); break;}
  159.     }
  160.   }, 500);
  161. }
  162. */