Skip to content

Instantly share code, notes, and snippets.

@edjw
Last active August 11, 2024 03:39
Show Gist options
  • Save edjw/61cda68535b237fa7fa8db23842c1b6d to your computer and use it in GitHub Desktop.
Save edjw/61cda68535b237fa7fa8db23842c1b6d to your computer and use it in GitHub Desktop.
// // https://twitter.com/settings/your_twitter_data/twitter_interests
// Lots of discussion and improvement of the original script in the comments
// Here's the best version that works well
// Because Twitter stops the script working after unchecking 50 interests, so you will almost certainly have to run the script multiple times
function sleep(milliseconds) {
return new Promise(function (resolve) {
return setTimeout(resolve, milliseconds);
});
};
Array.from(document
.querySelectorAll('input[type="checkbox"]'))
.filter(checkbox => checkbox.checked == true)
.slice(0, 50)
.forEach((checkbox, index, array) =>
sleep(1000).then(() => {
console.log(`${index + 1}/${array.length}: Unchecking "${checkbox.parentNode.parentNode.innerText}"`)
checkbox.click();
}));
@dnwe
Copy link

dnwe commented Nov 24, 2019

Thanks!

Bookmarklet version (which works on iOS etc without developer tools)

javascript:(function()%7Bjavascript%3A(function()%257B%2522use%2520strict%253B%2522%253Bconst%2520labelSelector%253D%2522.r-1p0dtai.r-1ei5mc7.r-1pi2tsx.r-1d2f490.r-crgep1.r-orgf3d.r-t60dpp.r-u8s1d.r-zchlnj.r-ipm5af.r-13qz1uu%2522%253Bconst%2520interests%253DArray.from(document.querySelectorAll(labelSelector))%253Binterests.forEach((interest)%253D%253E%257Bif(interest.checked)%257Binterest.click()%257D%257D)%257D)()%7D)()
  1. Open https://twitter.com/settings/your_twitter_data/twitter_interests
  2. Add to Favourites (bookmark)
  3. Edit the url or your saved bookmark and replace it with the javascript: one-liner
  4. "Open" the favourite whilst still on the original twitter interests page and it'll run the JavaScript against it

@Mike-T-Mitchell
Copy link

Mike-T-Mitchell commented Apr 11, 2020

Thanks so much for this. In my case, the labelSelector classnames needed to be re-pasted from the DOM (though I couldn't actually see a difference, so maybe something failed the first time I ran it, I don't know ¯\_(ツ)_/¯), but this worked a treat for me. I'll just say it's "interesting" that they'd make folks manually uncheck 500+ interests, banking on laziness to ensure they get the data they want.

@inactivist
Copy link

maybe something failed the first time I ran it

It appears Twitter throttles the backend API during a rolling time window - after a certain threshold is crossed during that time window, the API starts to return a 500 error (check your console or network XHR traffic and you'll see them).

So in my experience you need to repeat the process as every so often (15 minute intervals?) in order to remove all selected interests.

@Mike-T-Mitchell
Copy link

Mike-T-Mitchell commented Apr 14, 2020

@inactivist wow, that's wild. you're correct. tons of network calls returning 500s after I run the script. I'm seeing the checkboxes themselves become unchecked in the UI (even on refresh) though, so I assume this still worked properly? Will log out of/back into Twitter and see if those interests remain unchecked.

@edjw
Copy link
Author

edjw commented Apr 15, 2020

Thanks for investigating this everyone!

I added a counter into the script and found the 500 statuses seem to happen after about 48 checkboxes are unchecked. Your mileage may vary? I repeated a few times and the script didn't find any more checked boxes in the UI.

@inactivist
Copy link

@inactivist wow, that's wild. you're correct. tons of network calls returning 500s after I run the script. I'm seeing the checkboxes themselves become unchecked in the UI (even on refresh) though, so I assume this still worked properly? Will log out of/back into Twitter and see if those interests remain unchecked.

Yeah, it's somewhere around 50, and the visible checkboxes are unchecked, ignoring the API call result. Funny how that works out. Can you imagine someone manually unchecking hundreds of checkboxes and finding most are still checked next time they load the page?

I've experimented with using a random-ish interval between clicks in my python/selenium scripts, and it doesn't seem to matter - errors start to appear at around 50 API calls (as a result of the clicks).

@inactivist
Copy link

Thanks for investigating this everyone!

I added a counter into the script and found the 500 statuses seem to happen after about 48 checkboxes are unchecked. Your mileage may vary? I repeated a few times and the script didn't find any more checked boxes in the UI.

@edjw see my comment above -- that's consistent with my experience using python/selenium and chromedriver.

@inactivist
Copy link

I repeated a few times and the script didn't find any more checked boxes in the UI.

You must reload the page in order for the checkboxes to reflect the current status - the React app state isn't updated to reflect the backend API 500 errors, so the checkboxes are unchecked despite the failed API call.

I use a standalone python/selenium script I run periodically from a shell, I can share that if it seems helpful.

@ibrahimab
Copy link

ibrahimab commented Sep 20, 2020

Easier, more readable script and will only perform it for max 50 interests, because of rate limits. For more than 50 interests, you should wait 5 minutes before running it again.

Keep in mind twitter does not delete your interests and has no api endpoint to do this It just disables interests.

document
  .querySelectorAll('input[type="checkbox"]')
  .filter(checkbox => checkbox.checked == true)
  .slice(0, 50)
  .forEach(checkbox => checkbox.click());

@soctty
Copy link

soctty commented Nov 22, 2020

@inactivist wow, that's wild. you're correct. tons of network calls returning 500s after I run the script. I'm seeing the checkboxes themselves become unchecked in the UI (even on refresh) though, so I assume this still worked properly? Will log out of/back into Twitter and see if those interests remain unchecked.

It'll pull this nonsense after one uncheck. They really don't like users disabling these things.

@Rezzenonne
Copy link

Thanks!

Bookmarklet version (which works on iOS etc without developer tools)

javascript:(function()%7Bjavascript%3A(function()%257B%2522use%2520strict%253B%2522%253Bconst%2520labelSelector%253D%2522.r-1p0dtai.r-1ei5mc7.r-1pi2tsx.r-1d2f490.r-crgep1.r-orgf3d.r-t60dpp.r-u8s1d.r-zchlnj.r-ipm5af.r-13qz1uu%2522%253Bconst%2520interests%253DArray.from(document.querySelectorAll(labelSelector))%253Binterests.forEach((interest)%253D%253E%257Bif(interest.checked)%257Binterest.click()%257D%257D)%257D)()%7D)()
  1. Open https://twitter.com/settings/your_twitter_data/twitter_interests
  2. Add to Favourites (bookmark)
  3. Edit the url or your saved bookmark and replace it with the javascript: one-liner
  4. "Open" the favourite whilst still on the original twitter interests page and it'll run the JavaScript against it

Unfortunately, this did not work in Chrome or Edge.

@VaelophisNyx
Copy link

It'll pull this nonsense after one uncheck. They really don't like users disabling these things.

Yeah, just manually did ~90 of them and twitter took a solid 15 minutes with the page open and un-refreshed to clear them all.

To be fair they use it for forced tailored ads. But, that's specifically why I'd want them gone.

That said, wish I'd found this script sooner

@0rch
Copy link

0rch commented Dec 22, 2020

I am not so internet savvy that I understand what any of you are saying - but I am greatly consoled to see this effort of investigating what/why twitter is doing what they are doing. I am one of those people who went through the long list and unchecked each box, and then a day later wondered why I was still being recommended topics on my home timeline. More than 2/3 of the boxes were re-checked, and any effort to continue unchecking boxes prompts me with this error message at the bottom of my screen:

"Twitter is at capacity. Please wait a few moments and try again." Even if I've only unchecked one box, about 10 seconds later I'm shown this.

I'm offended at how blatantly twitter disregards their users wishes for an untampered experience. To me, it's appalling. I'm going to keep watch of this thread to hopefully see that one of you have come up with a bypass to this awful data trap design. Thanks in advance to you guys.

@mirabilos
Copy link

mirabilos commented Dec 27, 2020

Can you imagine someone manually unchecking hundreds of checkboxes and finding most are still checked next time they load the page?

Yeah… imagine my anger…

… so, how do I run this? Copy/paste the entire script into the developer console? (This is with Firefox)

Update: apparently, yes. Though periodically a popup about a script making the site slow appears… sigh but better than nothing.

Update 2: wow, the long script from this gist actually worked! My “interests” are empty, as they should be!

Here’s to hoping I won’t be annoyed with “topics” every third tweet now.

Update 3: after three days, Twitter has registred me with no less than 196 new “interests”. I guess I have to run this script every day…

@mirabilos
Copy link

mirabilos commented Dec 27, 2020

@ibrahimab Uncaught TypeError: document.querySelectorAll(...).filter is not a function for your short scriptlet.

@edjw
Copy link
Author

edjw commented Jan 21, 2021

@mirabilos. It was missing Array.from() around the document.querySelectorAll(...)

I've updated the gist with @ibrahimab's cleaner version with the Array.from() fix as well as a sleep function that might help some people with timeouts and some nicer logging

@trap15
Copy link

trap15 commented Jul 9, 2021

You can avoid the API lockout by making sure the wait is actually between each unchecking, and extending the timeout as well. With the below I could do 100 in one go, maybe a longer delay would let you go longer (or possibly it's because I've been messing with this for a bit).

Change

        sleep(1000).then(() => {

To

        sleep(5000 * index).then(() => {

@mirabilos
Copy link

mirabilos commented Jul 9, 2021 via email

@trap15
Copy link

trap15 commented Jul 9, 2021

Yes, but if you aren't doing this multiplication then every execution starts at the same time, just offset by 1000 from when the script is executed. Only with the multiplication is it delaying the same amount between each click, to simulate clicking them manually.

@mirabilos
Copy link

mirabilos commented Jul 9, 2021 via email

@ibrahimab
Copy link

Rewrote it to divide the checkboxes into chunks to run the script in batches to prevent hitting twitter's API rate limits.

let chunks = [];
let source = Array.from(document.querySelectorAll('input[type="checkbox"]:checked'));

while (source.length) {
  chunks.push(source.splice(0, 20));
}

const event = new MouseEvent('click', {

  view: window,
  bubbles: true,
  cancelable: true
});

let minutes = 10; // amount of time to wait between batches
let interval = setInterval(() => {

  if (chunks.length === 0) {

    clearInterval(interval);
    return;
  }

  chunks.splice(0, 1)[0].forEach(i => {
      i.dispatchEvent(event);
  });

}, minutes * 60 * 1000);

@ibrahimab
Copy link

People should realize that twitter does not remove the interests, and will continue to add/enable interests whilst using twitter.

@climardo
Copy link

Almost funny how this excellent gist is not the first result in Google, but it is the first result in Brave Search for "twitter clear interests"

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