-
Star
(398)
You must be signed in to star a gist -
Fork
(107)
You must be signed in to fork a gist
-
-
Save JamieMason/7580315 to your computer and use it in GitHub Desktop.
| // Unfollow everyone on twitter.com, by Jamie Mason (https://twitter.com/fold_left) | |
| // 1. Go to https://twitter.com/following. | |
| // 2. Keep scrolling to the bottom repeatedly until all your followers are loaded. | |
| // 3. Run this in your console. | |
| [].slice.call(document.querySelectorAll('.unfollow-text')).forEach(function(button) { | |
| button.click(); | |
| }); | |
| // If your browser is freezing because you follow *loads* of people, then try the version | |
| // in this comment: https://gist.github.com/JamieMason/7580315#gistcomment-2316858 |
Hello. Is it possible to make such a script for bluesky?
I've never used bluesky but I would expect someone could do this, these kinds of scripts can be written for most kinds of web pages.
Ctrl+Alt+J in chrome to open the Developer Tools console, then paste the code (ctrl+V) on the “following” page of your twitter account, and hit enter.
@isinguard thanks but I don't mean twitter.
I meant github followings.
Thanks
Script only for Twitter
slight change so it only unfollows people who don't follow you back: https://gist.github.com/darraghoriordan/1f8ddff7b12a68c259a1f71bf4000c51
Nice, @darraghoriordan 👍
Is there a way to get this not to unfollow private accounts?
Thank you very much! I successfully unfollowed 1969 in my twitter/x account! I made a github acc just to thank you!
You're welcome @crimsoniv0j, glad it worked out.
Still works! Thank you so much. Is there a code to follow massively on Twitter?
Great @niklas44f1. You could probably modify the script slightly so it works on someone's Following page for example, and clicks follow instead of unfollow or something. I'm not going to do it but it should be a fairly small job for someone.
still work thx
How to unfollow only unlocked (e.g. not private) accounts? The data id is data-testid="icon-lock"
Works. Thanks!
thank you very much , its working fine.
Thanks :) Its working FIne
work
Still working, amazing!
Thank you!
Thanks!
works thanks
Still works thanks
Thanks man still working
Working 😍
Woow works fine. huge thanks.
Still works. Thanks a lot!
The date today is November 19th, 2025. Does this still work.. anyone know?
Give it a try
Tested on 2025/12/20. Works!
Tested on 6th Jan 2026. Didnt work on Macos.
Added a few helpful things turn DRY_RUN to false to check compat. Set UNFOLLOW_LIMIT to a number that you want to try.
Thanks to the main author for the helpful script.
Script
// Unfollow everyone on twitter.com, by Jamie Mason (https://twitter.com/fold_left)
// https://gist.github.com/JamieMason/7580315
//
// 1. Go to https://twitter.com/YOUR_USER_NAME/following
// 2. Open the Developer Console. (COMMAND+ALT+I on Mac)
// 3. Paste this into the Developer Console and run it
//
// https://gist.github.com/JamieMason/7580315?permalink_comment_id=5932205#gistcomment-5932205
// IMPORTANT
const DO_NOT_UNFOLLOW = ['usernames_to_avoid_unfollowing'].map(s => s.toLowerCase());
(() => {
const DRY_RUN = true;
const UNFOLLOW_LIMIT = 10;
const FOLLOW_BUTTON_SELECTOR =
'[data-testid$="-unfollow"], button[aria-label*="Following"], button[aria-label*="Unfollow"]';
const CONFIRM_BUTTON_SELECTOR =
'[data-testid="confirmationSheetConfirm"], button[role="menuitem"]';
const retry = { count: 0, limit: 3 };
let unfollowedCount = 0;
const sleep = ms => new Promise(res => setTimeout(res, ms));
const scrollToBottom = () => window.scrollTo(0, document.body.scrollHeight);
const extractHandleFromHref = href => {
if (!href) return null;
const parts = href.split('?')[0].split('/').filter(Boolean);
const candidate = parts.at(-1)?.replace(/^@/, '');
return /^[A-Za-z0-9_]{2,50}$/.test(candidate) ? candidate.toLowerCase() : null;
};
const findHandleNearButton = button => {
const aria = button.getAttribute?.('aria-label') || '';
const ariaMatch = aria.match(/@([A-Za-z0-9_]{2,50})/);
if (ariaMatch) return ariaMatch[1].toLowerCase();
let el = button;
for (let i = 0; i < 6 && el; i++) {
for (const a of el.querySelectorAll?.('a[href]') || []) {
const h = extractHandleFromHref(a.getAttribute('href'));
if (h) return h;
}
el = el.parentElement;
}
const container = button.closest('div') || button.parentElement;
if (container) {
for (const n of container.querySelectorAll('span, a, div')) {
const t = (n.textContent || '').trim();
const m = t.match(/^@([A-Za-z0-9_]{2,50})$/) || t.match(/@([A-Za-z0-9_]{2,50})/);
if (m) return m[1].toLowerCase();
}
}
return null;
};
const shouldSkip = handle => handle && DO_NOT_UNFOLLOW.includes(handle);
const unfollowSingle = async (button, handle) => {
if (unfollowedCount >= UNFOLLOW_LIMIT) return false;
if (DRY_RUN) {
console.log(`[DRY-RUN] would unfollow (${unfollowedCount + 1}/${UNFOLLOW_LIMIT}): ${handle || '(unknown)'}`);
unfollowedCount++;
return true;
}
button.scrollIntoView({ block: 'center' });
await sleep(200);
button.click();
await sleep(500);
const confirm = document.querySelector(CONFIRM_BUTTON_SELECTOR);
if (confirm) confirm.click();
await sleep(300);
unfollowedCount++;
console.log(`[LIVE] unfollowed (${unfollowedCount}/${UNFOLLOW_LIMIT}): ${handle || '(unknown)'}`);
return true;
};
const processVisibleButtons = async processed => {
const buttons = [...document.querySelectorAll(FOLLOW_BUTTON_SELECTOR)];
for (const btn of buttons) {
if (unfollowedCount >= UNFOLLOW_LIMIT) return true;
const handle = findHandleNearButton(btn);
if (handle && processed.has(handle)) continue;
if (handle) processed.add(handle);
if (shouldSkip(handle)) {
console.log(`[PROTECTED] skipping ${handle}`);
continue;
}
await unfollowSingle(btn, handle);
await sleep(600);
}
return false;
};
(async () => {
console.log(
`START | mode=${DRY_RUN ? 'DRY-RUN' : 'LIVE'} | limit=${UNFOLLOW_LIMIT}`
);
const processed = new Set();
while (retry.count < retry.limit && unfollowedCount < UNFOLLOW_LIMIT) {
const limitReached = await processVisibleButtons(processed);
if (limitReached) break;
retry.count++;
scrollToBottom();
await sleep(1500);
}
console.log(
`DONE | mode=${DRY_RUN ? 'DRY-RUN' : 'LIVE'} | total=${unfollowedCount}/${UNFOLLOW_LIMIT}`
);
})();
})();It runs well. Great Job! THX
Hello. Is it possible to make such a script for bluesky?