From 5cab38af6d48bbb50e9c368b1af242412d39fdac Mon Sep 17 00:00:00 2001 From: dataprolet <1+dataprolet@noreply.localhost> Date: Mon, 11 May 2026 11:50:24 +0200 Subject: [PATCH] Add unfollow.py --- unfollow.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 unfollow.py diff --git a/unfollow.py b/unfollow.py new file mode 100644 index 0000000..724a155 --- /dev/null +++ b/unfollow.py @@ -0,0 +1,77 @@ +from datetime import timezone +from atproto import Client +import datetime + +from telegram import Bot +import asyncio + +client = Client() +handle = '' +password = '' +client.login(handle, password) + +BOT_TOKEN = '' +CHAT_ID = '' + +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 action_on_users(follows, days, repost, dry=True): + """Identify users to unfollow based on posting activity.""" + for user in follows: + if is_inactive(user, days): + if not dry: + unfollow(user,days) + + +async def send_message(user,days): + bot = Bot(token=BOT_TOKEN) + await bot.send_message(chat_id=CHAT_ID, text=f"Unfollow {user.display_name} ({user.handle}): {days} days inactive.") + + +def unfollow(user,days): + """Unfollow users.""" + client.delete_follow(user.viewer.following) + asyncio.run(send_message(user,days)) + + +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.") + + args = parser.parse_args() + recursive = args.prod or args.dry + dry = args.dry + prod = args.prod + main(recursive, dry, prod, args.days) \ No newline at end of file