import os
import sys
import argparse
from urllib.parse import urlparse
from dotenv import load_dotenv
from atproto import Client, models
from atproto_client.exceptions import BadRequestError
# Load environment variables from .env file
load_dotenv()
# Initialize the client
client = Client()
# Login using credentials from .env file
client.login(os.getenv('BSKY_HANDLE'), os.getenv('BSKY_PASSWORD'))
def extract_post_info(url):
parsed_url = urlparse(url)
path_parts = parsed_url.path.split('/')
if len(path_parts) < 4 or path_parts[1] != 'profile':
raise ValueError("Invalid Bluesky post URL")
return path_parts[2], path_parts[4]
def get_author_did(handle):
try:
profile = client.app.bsky.actor.get_profile({'actor': handle})
return profile.did
except Exception as e:
print(f"Error getting author DID: {e}")
return None
def get_likes(uri, cursor=None):
try:
response = client.app.bsky.feed.get_likes({'uri': uri, 'cursor': cursor})
return response
except BadRequestError as e:
print(f"Error: {e}")
return None
def block_user(did, dry_run=False):
try:
if not dry_run:
#client.app.bsky.graph.block({'subject': did})
print(f"Blocked user: {did}")
else:
print(f"Would block user: {did}")
except Exception as e:
print(f"Error blocking user {did}: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Fetch likes for a Bluesky post and optionally block users.")
parser.add_argument("post_url", help="URL of the Bluesky post")
parser.add_argument("--block", action="store_true", help="Block all users who liked the post")
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without actually blocking users")
args = parser.parse_args()
try:
author_handle, post_rkey = extract_post_info(args.post_url)
except ValueError as e:
print(f"Error: {e}")
sys.exit(1)
author_did = get_author_did(author_handle)
if not author_did:
print("Could not fetch author's DID. Exiting.")
sys.exit(1)
post_uri = f"at://{author_did}/app.bsky.feed.post/{post_rkey}"
print(f"Post URI: {post_uri}")
all_likes = []
cursor = None
while True:
response = get_likes(post_uri, cursor)
if response is None:
break
all_likes.extend(response.likes)
if response.cursor:
cursor = response.cursor
else:
break
print("\nUsers who liked the post:")
for like in all_likes:
print(f"User: {like.actor.handle}")
print(f"\nTotal likes: {len(all_likes)}")
if args.block:
print("\nBlocking users:")
for like in all_likes:
block_user(like.actor.did, args.dry_run)
if args.dry_run:
print("\nThis was a dry run. No users were actually blocked.")
## Just list likes:
# python script.py <post_url>
## List likes and block users:
# python script.py <post_url> --block
## List likes and simulate blocking (dry run):
# python script.py <post_url> --block --dry-run