A third party ATProto appview
1#!/bin/bash
2
3# A script to authenticate with Bluesky and fetch the user's timeline.
4# This version automatically discovers the user's PDS.
5#
6# USAGE:
7# ./fetch_timeline.sh your-handle.bsky.social your-app-password
8#
9# DEPENDENCIES:
10# - curl: For making HTTP requests.
11# - jq: For parsing JSON responses. (Install with: sudo apt-get install jq / brew install jq)
12
13set -e # Exit immediately if any command fails
14
15# --- CONFIGURATION ---
16# The AppView to fetch the timeline from.
17# Set this to your custom AppView for testing or leave as the official one.
18APPVIEW_HOST="https://appview.dollspace.gay"
19
20# --- 1. CHECK DEPENDENCIES & ARGUMENTS ---
21
22# Check for jq
23if ! command -v jq &> /dev/null; then
24 echo "Error: 'jq' is not installed. Please install it to proceed."
25 echo "e.g., sudo apt-get install jq or brew install jq"
26 exit 1
27fi
28
29# Check for curl
30if ! command -v curl &> /dev/null; then
31 echo "Error: 'curl' is not installed. Please install it to proceed."
32 exit 1
33fi
34
35# Check for command-line arguments
36if [ "$#" -ne 2 ]; then
37 echo "Usage: $0 <handle> <app-password>"
38 exit 1
39fi
40
41HANDLE=$1
42APP_PASSWORD=$2
43
44# --- 2. RESOLVE HANDLE TO FIND PDS ---
45
46echo "🔎 Resolving handle '$HANDLE' to find user's PDS..."
47
48# Step 2a: Resolve the handle to get the user's DID
49# This asks a public server (bsky.social) what DID corresponds to the handle.
50DID_RESPONSE=$(curl -s "https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=$HANDLE")
51DID=$(echo "$DID_RESPONSE" | jq -r '.did')
52
53if [ "$DID" == "null" ] || [ -z "$DID" ]; then
54 echo "❌ Error: Could not resolve handle '$HANDLE' to a DID."
55 echo "Server response:"
56 echo "$DID_RESPONSE" | jq .
57 exit 1
58fi
59
60echo "✅ Found DID: $DID"
61
62# Step 2b: Use the DID to look up the user's DID Document from the PLC directory
63# The DID Document contains the user's actual PDS address.
64DID_DOC=$(curl -s "https://plc.directory/$DID")
65PDS_HOST=$(echo "$DID_DOC" | jq -r '.service[] | select(.id == "#atproto_pds") | .serviceEndpoint')
66
67if [ "$PDS_HOST" == "null" ] || [ -z "$PDS_HOST" ]; then
68 echo "❌ Error: Could not find PDS host in DID document for $DID."
69 echo "DID Document:"
70 echo "$DID_DOC" | jq .
71 exit 1
72fi
73
74echo "✅ Found PDS Host: $PDS_HOST"
75
76# --- 3. AUTHENTICATE AND GET TOKEN ---
77
78echo "🔐 Authenticating with PDS: $PDS_HOST..."
79
80# Capture the full JSON response from the server first for better error handling
81SESSION_RESPONSE=$(curl -s -X POST \
82 -H "Content-Type: application/json" \
83 -d "{\"identifier\": \"$HANDLE\", \"password\": \"$APP_PASSWORD\"}" \
84 "$PDS_HOST/xrpc/com.atproto.server.createSession")
85
86# Now, extract the access token from the response
87TOKEN=$(echo "$SESSION_RESPONSE" | jq -r '.accessJwt' || echo "null")
88
89# Verify that we got a token
90if [ "$TOKEN" == "null" ] || [ -z "$TOKEN" ]; then
91 echo "❌ Authentication failed. Server response:"
92 echo "$SESSION_RESPONSE" | jq .
93 echo "Please check your handle and app password."
94 exit 1
95fi
96
97echo "✅ Successfully obtained access token."
98
99# --- 3a. DECODE TOKEN FOR DEBUGGING ---
100# This part is useful for verifying the contents of the token itself.
101# The 'sub' field should match the DID we found earlier.
102echo "--- DEBUG: Decoding Access Token Payload ---"
103DECODED_PAYLOAD=$(echo "$TOKEN" | jq -R 'split(".") | .[1] | @base64d | fromjson' 2>/dev/null || echo "{}")
104echo "$DECODED_PAYLOAD" | jq .
105echo "------------------------------------------"
106
107
108# --- 4. FETCH THE TIMELINE ---
109
110echo "📄 Fetching timeline from AppView: $APPVIEW_HOST..."
111
112# Use the captured token in the Authorization header to make an authenticated request.
113TIMELINE_RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" \
114 "$APPVIEW_HOST/xrpc/app.bsky.feed.getTimeline?limit=25")
115
116
117# --- 5. OUTPUT THE RESULT ---
118
119echo "--- TIMELINE RESPONSE ---"
120# Pipe the response to jq for pretty-printing if it's valid JSON
121if echo "$TIMELINE_RESPONSE" | jq . &> /dev/null; then
122 echo "$TIMELINE_RESPONSE" | jq .
123else
124 echo "❌ Failed to fetch timeline or response was not valid JSON:"
125 echo "$TIMELINE_RESPONSE"
126fi