Files
Bluesky-Unfollow-Inactive/unfollow.py
T
2026-05-11 11:50:24 +02:00

77 lines
2.5 KiB
Python

from datetime import timezone
from atproto import Client
import datetime
from telegram import Bot
import asyncio
client = Client()
handle = '<YOUR-HANDLE>'
password = '<YOUR-APP-PASSWORD>'
client.login(handle, password)
BOT_TOKEN = '<YOUR-BOT-TOKEN>'
CHAT_ID = '<YOUR-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)