Last active
December 3, 2024 13:47
-
-
Save PavlikPolivka/58070bb2ec5b04eb6f47ad37246b8eff to your computer and use it in GitHub Desktop.
SkyBlue cleanup script to unfollow inactive accounts. Fill in your handle and app password and run it like python unfollow.py --prod --days 15. It will unfollow all acounts that were not active in last 15 days.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import timezone | |
from atproto import Client | |
import datetime | |
client = Client() | |
handle = 'YOUR_HANLDE' | |
password = 'YOUR_APP_PASSWORD' | |
client.login(handle, password) | |
def get_follows(recursive=False): | |
"""Fetch the list of people you are currently following.""" | |
follows = [] | |
limit = 100 if recursive else 1 | |
response = client.get_follows(handle, None, limit) | |
follows.extend(response.follows) | |
while recursive and response.cursor: | |
response = client.get_follows(handle, response.cursor, 100) | |
follows.extend(response.follows) | |
return follows | |
def get_last_post_date(user): | |
"""Fetch the date of the last post of a user.""" | |
response = client.get_author_feed(user.handle) | |
if len(response.feed) > 0: | |
return datetime.datetime.fromisoformat(response.feed[0].post.indexed_at) | |
return None | |
def is_inactive(user, days=15): | |
fifteen_days_ago = datetime.datetime.now(timezone.utc) - datetime.timedelta(days=days) | |
last_post_day = get_last_post_date(user) | |
return last_post_day is None or last_post_day < fifteen_days_ago | |
def is_reposter(user, ratio=0.8): | |
"""Check if a user is a reposter.""" | |
response = client.get_author_feed(user.handle) | |
reposts = 0 | |
for post in response.feed: | |
if post.post.author.handle != user.handle: | |
reposts += 1 | |
actual_ration = reposts / len(response.feed) | |
return actual_ration > ratio | |
def action_on_users(follows, days, repost, dry=True): | |
"""Identify users to unfollow based on posting activity.""" | |
inactiveCount = 0 | |
reposterCount = 0 | |
for user in follows: | |
if is_inactive(user, days): | |
inactiveCount += 1 | |
print(f"User {user.handle} has not posted in the last {days} days.") | |
if not dry: | |
unfollow(user) | |
if is_reposter(user, repost): | |
reposterCount += 1 | |
print(f"User {user.handle} is a reposter.") | |
if not dry: | |
unfollow(user) | |
if dry: | |
print(f"Would unfollow {inactiveCount} inactive users and {reposterCount} reposters.") | |
else: | |
print(f"Unfollowed {inactiveCount} inactive users and {reposterCount} reposters.") | |
def unfollow(user): | |
"""Unfollow users.""" | |
client.delete_follow(user.viewer.following) | |
def main(recursive=False, dry=False, prod=False, days=15, repost=0.8): | |
follows = get_follows(recursive) | |
action_on_users(follows, days, repost, not prod) | |
if __name__ == "__main__": | |
import argparse | |
parser = argparse.ArgumentParser(description="Unfollow inactive users.") | |
parser.add_argument('--dry', action='store_true', help="Full run, dry unfollow.") | |
parser.add_argument('--prod', action='store_true', help="Full run. Actually unfollow.") | |
parser.add_argument('--days', type=int, default=15, help="Number of days of inactivity to consider for unfollowing.") | |
parser.add_argument('--repost', type=float, default=0.8, help="Ratio of reposts.") | |
args = parser.parse_args() | |
recursive = args.prod or args.dry | |
dry = args.dry | |
prod = args.prod | |
main(recursive, dry, prod, args.days) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment