55 lines
1.3 KiB
Python
55 lines
1.3 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
import argparse
|
||
|
|
from atproto import Client
|
||
|
|
|
||
|
|
DEFAULT_DOMAIN = "bsky.social"
|
||
|
|
DEFAULT_TOKEN_FILE = "/tmp/bs/last_token"
|
||
|
|
|
||
|
|
def load_token(token_file=DEFAULT_TOKEN_FILE):
|
||
|
|
try:
|
||
|
|
with open(token_file, "r") as f:
|
||
|
|
return f.read().strip()
|
||
|
|
except FileNotFoundError:
|
||
|
|
return None
|
||
|
|
|
||
|
|
def fetch_post_uris(handle, token=None):
|
||
|
|
client = Client()
|
||
|
|
if token:
|
||
|
|
client.login_with_access_token(token)
|
||
|
|
|
||
|
|
# Resolve handle to DID
|
||
|
|
did = client.resolve_handle(handle).did
|
||
|
|
|
||
|
|
uris = []
|
||
|
|
limit = 50
|
||
|
|
cursor = None
|
||
|
|
|
||
|
|
while True:
|
||
|
|
resp = client.app.bsky.feed.get_author_feed(
|
||
|
|
actor=did, limit=limit, cursor=cursor
|
||
|
|
)
|
||
|
|
for post in resp.data.feed:
|
||
|
|
uris.append(post.post.uri)
|
||
|
|
|
||
|
|
if not resp.data.cursor:
|
||
|
|
break
|
||
|
|
cursor = resp.data.cursor
|
||
|
|
|
||
|
|
return uris
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(description="Fetch all post URIs of a Bluesky profile")
|
||
|
|
parser.add_argument("handle", help="Bluesky handle (ex: alice.bsky.social)")
|
||
|
|
parser.add_argument("--token-file", default=DEFAULT_TOKEN_FILE, help="File storing access token")
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
token = load_token(args.token_file)
|
||
|
|
uris = fetch_post_uris(args.handle, token)
|
||
|
|
|
||
|
|
for uri in uris:
|
||
|
|
print(uri)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|
||
|
|
|