Skip to content

Instantly share code, notes, and snippets.

@adminy
Last active January 18, 2023 04:49
Show Gist options
  • Save adminy/5e80c40592b135e6d7fd8e6bd88c825a to your computer and use it in GitHub Desktop.
Save adminy/5e80c40592b135e6d7fd8e6bd88c825a to your computer and use it in GitHub Desktop.
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1) + min) // min and max included
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const followersElement = getFollowersElementWithUsername(getUsername())
followersElement.click()
function getUsername () {
const pageTitleElement = document.getElementsByTagName('h2')[0]
if (!pageTitleElement) throw new Error('No title to get username from')
return pageTitleElement.innerHTML
}
function getFollowersElementWithUsername (username) {
const followersElement = document.querySelectorAll('a[href="/' + username + '/following/"]')[0]
if (!followersElement) throw new Error('No followers element was found')
return followersElement
}
const friends = new Set(['user1', 'user2'])
const unfollows = {}
let tries = 0
let globalCount = 0
const hasMoreToLoad = ul => ul.childElementCount !== globalCount || ul.children[ul.childElementCount - 1].firstChild.getAttribute('data-visualcompletion') === 'loading-state'
const populateUnfollowsPool = () => {
const ul = document.querySelectorAll('ul')[1].firstChild
const lis = [...ul.querySelectorAll('li')]
.map(li => ({ button: li.querySelector('button'), name: (li.querySelector('a') || { href: '' }).href.split('/').slice(-2)[0] }))
.filter(({ button, name }) => button && button.textContent === 'Following' && name && !friends.has(name) && !unfollows[name])
lis.forEach(({ button, name }) => (unfollows[name] = { button, name }))
globalCount = ul.childElementCount
ul.children[globalCount - 1].scrollIntoView(true)
console.log('elements', globalCount, 'unfollows', Object.keys(unfollows).length, 'tries', tries)
setTimeout(() => hasMoreToLoad(ul) || tries++ < 3 ? populateUnfollowsPool() : finishedCollection(), randInt(848, 2004))
}
setTimeout(populateUnfollowsPool, 3000)
async function finishedCollection () {
console.log('!!! Collection Finished')
const toUnfollow = Object.values(unfollows)
const total = toUnfollow.length
for (let i = 0; i < total; i++) {
const { button, name } = toUnfollow[i]
button.scrollIntoView(true)
button.click()
await sleep(randInt(50, 200))
const unfollow = [...document.querySelectorAll('button')].find(button => button.textContent === 'Unfollow')
unfollow && unfollow.click()
console.log('Unfollowed ', name, (i / total * 100).toFixed(2), '%')
await sleep(i % 8 ? randInt(120, 800) : randInt(1, 10) * 60 * 1000)
}
}
@boxxxxy
Copy link

boxxxxy commented Dec 19, 2021

It's now unfollowing correctly after a few refreshes and script restarts, the api limit keep getting hit but after 5 mins it'll unfollow a couple more so it's ok!

@koolamusic
Copy link

Thanks for making this.

@mxxnshade
Copy link

keep getting
`Vev1H1eefJG.js?_nc_x=Ij3Wp8lg5Kz:59 ErrorUtils caught an error:

Cannot read properties of undefined (reading 'scrollIntoView')

Subsequent non-fatal errors won't be logged; see https://fburl.com/debugjs.`

@mikeknapp
Copy link

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