Skip to content

Instantly share code, notes, and snippets.

@IanColdwater
Last active October 13, 2024 04:40
Show Gist options
  • Save IanColdwater/88b3341a7c4c0cf71c73ac56f9bd36ec to your computer and use it in GitHub Desktop.
Save IanColdwater/88b3341a7c4c0cf71c73ac56f9bd36ec to your computer and use it in GitHub Desktop.
Here are some terms to mute on Twitter to clean your timeline up a bit.
Mute these words in your settings here: https://twitter.com/settings/muted_keywords
ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline
@ksoda
Copy link

ksoda commented Nov 21, 2021

It just works

location.assign("https://twitter.com/settings/muted_keywords");
// wait...

// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set;
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(
    Object.getPrototypeOf(element),
    "value"
  ).set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

function delay(f = 1) {
  return new Promise((res) => setTimeout(res, f * 1000));
}

`ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline`
  .split(/\W+/)
  .reduce(async function go(prev, keyword) {
    await prev;

    document.querySelector('[aria-label="Add muted word or phrase"]').click();
    await delay();

    const el = document.querySelector("input[name=keyword]");
    setNativeValue(el, keyword);
    el.dispatchEvent(new Event("input", { bubbles: true }));
    await delay();

    document
      .evaluate(
        '//*[contains(text(), "Save")]/ancestor::*[@role = "button"]',
        document,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
      )
      .snapshotItem(0)
      .click();
    return delay();
  }, Promise.resolve());

I didn't even see the behavior change

@EpicLPer
Copy link

EpicLPer commented Mar 2, 2022

Still not sure if this actually works still or not.

@schuster
Copy link

schuster commented Mar 3, 2022

I never got this working again after it stopped working for me over a year ago, but I found a better option for me: you can switch to chronological ordering in the mobile app by tapping the star icon in the upper-right, then tapping "switch to latest tweets". The chronological mode doesn't show any of the suggested tweets, likes, etc., which is exactly what I want.

@mwbrooks
Copy link

mwbrooks commented Mar 3, 2022

Very helpful @schuster, this is exactly what I wanted as well. Thanks! 🙇

@Nin10Doug
Copy link

Nin10Doug commented Jun 30, 2022

Wish to know what each of these actually do. I've seen some of em remove replies from the same user who made the main tweet, but I'm unsure which. Would be nice to know what each one did. Or if this are some that are missing too.

@freezetreat
Copy link

It just works

location.assign("https://twitter.com/settings/muted_keywords");
// wait...

// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set;
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(
    Object.getPrototypeOf(element),
    "value"
  ).set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

function delay(f = 1) {
  return new Promise((res) => setTimeout(res, f * 1000));
}

`ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline`
  .split(/\W+/)
  .reduce(async function go(prev, keyword) {
    await prev;

    document.querySelector('[aria-label="Add muted word or phrase"]').click();
    await delay();

    const el = document.querySelector("input[name=keyword]");
    setNativeValue(el, keyword);
    el.dispatchEvent(new Event("input", { bubbles: true }));
    await delay();

    document
      .evaluate(
        '//*[contains(text(), "Save")]/ancestor::*[@role = "button"]',
        document,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
      )
      .snapshotItem(0)
      .click();
    return delay();
  }, Promise.resolve());

I didn't even see the behavior change

Doesn't work anymore

@ReneFennet
Copy link

ReneFennet commented Sep 11, 2022

Works like a charme:

Question. I still get "Related Tweets" at the end of a thread, if any. How do I silence those?

@bmorphism
Copy link

Perhaps it is time to add 🧵 - these threads are out of control!

@heavyoak
Copy link

Id like to know what some of these do before I copy paste them

@tobystic
Copy link

Perhaps it is time to add 🧵 - these threads are out of control!

it's so sad, but you don't want to throw the baby out with the bathwater. There's a good thread for every 10 trash threads

@siukalov
Copy link

Here is a minor update to this https://gist.github.com/IanColdwater/88b3341a7c4c0cf71c73ac56f9bd36ec?permalink_comment_id=3969640#gistcomment-3969640 solution. It adds a check for an already muted word

location.assign("https://twitter.com/settings/muted_keywords");
// wait...

// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set;
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(
    Object.getPrototypeOf(element),
    "value"
  ).set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

function delay(f = 1) {
  return new Promise((res) => setTimeout(res, f * 1000));
}

`ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline`
  .split(/\W+/)
  .reduce(async function go(prev, keyword) {
    await prev;

    document.querySelector('[aria-label="Add muted word or phrase"]').click();
    await delay();

    const el = document.querySelector("input[name=keyword]");
    setNativeValue(el, keyword);
    el.dispatchEvent(new Event("input", { bubbles: true }));
    await delay();

    const saveButton = document
    .evaluate(
      '//*[contains(text(), "Save")]/ancestor::*[@role = "button"]',
      document,
      null,
      XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
      null
    )

    saveButton.snapshotItem(0).click()
    await delay()

    // If the word has already been muted, click the back button
    if (document.querySelector('[aria-live="assertive"]')) {
      document.querySelector('[aria-label="Back"]').click();
    }

    return delay();
  }, Promise.resolve());

Copy link

ghost commented Feb 17, 2023

Here is a minor update to this https://gist.github.com/IanColdwater/88b3341a7c4c0cf71c73ac56f9bd36ec?permalink_comment_id=3969640#gistcomment-3969640 solution. It adds a check for an already muted word

location.assign("https://twitter.com/settings/muted_keywords");
// wait...

// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set;
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(
    Object.getPrototypeOf(element),
    "value"
  ).set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

function delay(f = 1) {
  return new Promise((res) => setTimeout(res, f * 1000));
}

`ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline`
  .split(/\W+/)
  .reduce(async function go(prev, keyword) {
    await prev;

    document.querySelector('[aria-label="Add muted word or phrase"]').click();
    await delay();

    const el = document.querySelector("input[name=keyword]");
    setNativeValue(el, keyword);
    el.dispatchEvent(new Event("input", { bubbles: true }));
    await delay();

    const saveButton = document
    .evaluate(
      '//*[contains(text(), "Save")]/ancestor::*[@role = "button"]',
      document,
      null,
      XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
      null
    )

    saveButton.snapshotItem(0).click()
    await delay()

    // If the word has already been muted, click the back button
    if (document.querySelector('[aria-live="assertive"]')) {
      document.querySelector('[aria-label="Back"]').click();
    }

    return delay();
  }, Promise.resolve());

How tf you apply those, i'm no git or web console expert :(

@abandon-the-flesh
Copy link

Here is a minor update to this https://gist.github.com/IanColdwater/88b3341a7c4c0cf71c73ac56f9bd36ec?permalink_comment_id=3969640#gistcomment-3969640 solution. It adds a check for an already muted word

location.assign("https://twitter.com/settings/muted_keywords");
// wait...

// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, "value").set;
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(
    Object.getPrototypeOf(element),
    "value"
  ).set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

function delay(f = 1) {
  return new Promise((res) => setTimeout(res, f * 1000));
}

`ActivityTweet
generic_activity_highlights
generic_activity_momentsbreaking
RankedOrganicTweet
suggest_activity
suggest_activity_feed
suggest_activity_highlights
suggest_activity_tweet
suggest_grouped_tweet_hashtag
suggest_pyle_tweet
suggest_ranked_organic_tweet
suggest_ranked_timeline_tweet
suggest_recap
suggest_recycled_tweet
suggest_recycled_tweet_inline
suggest_sc_tweet
suggest_timeline_tweet
suggest_who_to_follow
suggestactivitytweet
suggestpyletweet
suggestrecycledtweet_inline`
  .split(/\W+/)
  .reduce(async function go(prev, keyword) {
    await prev;

    document.querySelector('[aria-label="Add muted word or phrase"]').click();
    await delay();

    const el = document.querySelector("input[name=keyword]");
    setNativeValue(el, keyword);
    el.dispatchEvent(new Event("input", { bubbles: true }));
    await delay();

    const saveButton = document
    .evaluate(
      '//*[contains(text(), "Save")]/ancestor::*[@role = "button"]',
      document,
      null,
      XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
      null
    )

    saveButton.snapshotItem(0).click()
    await delay()

    // If the word has already been muted, click the back button
    if (document.querySelector('[aria-live="assertive"]')) {
      document.querySelector('[aria-label="Back"]').click();
    }

    return delay();
  }, Promise.resolve());

@siukalov This stopped working for me. I'm getting an error: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://ads-api.twitter.com/12/measurement/dcm_local_id. (Reason: CORS request did not succeed). Status code: (null).
Requesting help!

@orbitalflower
Copy link

orbitalflower commented Jul 6, 2023

Short answer: This mute list does not work, and has never worked. TylerBussell confirmed as much three years ago.

This has come up several times in the past. We made some changes to the timeline settings + someone released a JS plugin that did some blocking, so it caused some confusion and people thought muting via keyword mute did something.

Long version:

Prior to the July 2019 redesign of Twitter, these keywords appeared in CSS class names. You could write custom CSS to block these elements using a browser plugin like Stylus or an adblocker. Around 6 May 2017, Twitter started putting other user's Likes to users' timelines, resulting in people writing adblocker rules like this one and this one to block them. Rules would similarly be created to block promoted tweets, like this one.

At some point, someone misunderstood that this would work if you simply added the keyword to Twitter's mute list. The earliest example I can find is this Japanese tweet dated 18 July 2017, where the author claims that it works. This idea quickly spread through Japanese Twitter, and by the end of July it began to appear in English tweets. Even then, people were noticing that it didn't really work, like this tweet. The list of keywords grew as people discovered more CSS class names for unwanted Tweet types and added them to the mute lists, which spread virally on Twitter.

When the new Twitter landed in July 2019, there was an increase in demand to get rid of new junk, and so you saw articles like this one on 1 August 2019. There was now a long list of CSS class names, but new Twitter no longer even used those class names. By the time twittermute.txt was posted above in 2020, Twitter had long retired the version of web Twitter which used these keywords. Even if it had worked back then, it would not work in current Twitter.

People who report that it does work are falling prey to the placebo effect. Advertising and junk is actually removed in the short term by either refreshing the page or switching to the Latest timeline. Notice how earlier people in this thread are suggesting both of these solutions in addition to the mutelist. Several users for whom it originally appeared to work, later complained that it stopped working, which is consistent with the idea that refreshing the page alone is effective, and the mutelist does nothing.

The script which people are helpfully trying to fix does nothing but add these useless keywords to the mute list.

To be clear, if you're discovering this thread in 2023 or later, absolutely nothing in this thread is a correct system for removing the junk from Twitter. The mutelist does nothing, the script does nothing. The whole thing is a superstition.

@abandon-the-flesh
Copy link

abandon-the-flesh commented Jul 6, 2023

Short answer: This mute list does not work, and has never worked. TylerBussell confirmed as much three years ago.

@orbitalflower
Thank you for explaining that the word contents within the script do nothing. However, I'm simply trying to get the script itself to work to auto-add muted words other than the contents within the script, such as #ad, and adult/crypto related terms. Unless (like you said) I am going through some kind of placebo effect, or misremembering something, this script was applying new words to my muted list, and that is all I am looking for assistance with at this time.

@Etheonor
Copy link

@abandon-the-flesh
Hey, I know it's sound like an ad, but I had the exact same issue and wanted to add lists of muted words because for some reasons Twitter only allow to add them one by one...
I created a small browser extension where you can enter a URL from github gist or pastebin and automatically add dozens of muted words. Check it if you want to (beta)test, I'm the only user right now and need some feedback ^^ https://www.chirpsilencer.com/

@abandon-the-flesh
Copy link

I can confirm this works on chrome! I I do recall getting caught up with a maximum number of words I could mute which is unfortunate. For now I finished adding my list manually, but I just thought I'd mention this to you and hope it looks for duplicates. Hope it works for Firefox eventually

@Etheonor
Copy link

Glad to hear it.

As far as the limit is concerned, it does exist within Twitter, which is a bit impractical...

As soon as I have a bit of time I'll start converting the Firefox extension. For the moment, simply transferring from one to the other doesn't work and will require a few adjustments.

@Etheonor
Copy link

Mozilla just approved the plugin https://addons.mozilla.org/fr/firefox/addon/chirp-silencer/

Need some tests because I had to change some key elements in it...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment