Skip to content

Instantly share code, notes, and snippets.

@sriganesh
Created July 27, 2025 00:46
Show Gist options
  • Save sriganesh/cdb08435125278ad5b7c1f76f26c3e2d to your computer and use it in GitHub Desktop.
Save sriganesh/cdb08435125278ad5b7c1f76f26c3e2d to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import json
import time
import random
import requests
from PIL import Image
from io import BytesIO
BASE32_ALPHABET = "234567abcdefghijklmnopqrstuvwxyz"
def generate_tid():
timestamp_us = int(time.time() * 1_000_000)
clock_id = random.randint(0, 1023)
tid_int = (timestamp_us << 10) | clock_id
result = []
for _ in range(13):
result.append(BASE32_ALPHABET[tid_int & 0x1f])
tid_int >>= 5
return ''.join(reversed(result))
def main():
# Update these values
HANDLE = "sriganesh.bsky.social" #handle
PASSWORD = "abcd-efgh-ijkl-mnop" #app password
IMAGE_PATH = "img/spidermanblob.jpg" #change to your image
print("Starting...")
print(f"Handle: {HANDLE}")
# Login
print("\nLogging in...")
session_url = "https://bsky.social/xrpc/com.atproto.server.createSession"
login_response = requests.post(session_url, json={
"identifier": HANDLE,
"password": PASSWORD
})
if login_response.status_code != 200:
print(f"Login failed: {login_response.text}")
return
auth = login_response.json()
token = auth["accessJwt"]
did = auth["did"]
print(f"Logged in!")
print(f"DID: {did}")
# Upload image
print(f"\nProcessing image: {IMAGE_PATH}")
with Image.open(IMAGE_PATH) as img:
print(f"Original size: {img.size}")
print(f"Format: {img.format}")
if img.mode != 'RGB':
img = img.convert('RGB')
img.thumbnail((800, 800), Image.Resampling.LANCZOS)
print(f"Resized image: {img.size}")
output = BytesIO()
img.save(output, format='JPEG', quality=85)
image_data = output.getvalue()
print(f"Final size: {len(image_data):,}")
print("\nUploading image to PDS...")
upload_response = requests.post(
"https://bsky.social/xrpc/com.atproto.repo.uploadBlob",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "image/jpeg"
},
data=image_data
)
if upload_response.status_code != 200:
print(f"Upload failed: {upload_response.text}")
return
blob = upload_response.json()["blob"]
print(f"Image uploaded!")
print(f"Blob: {blob['ref']['$link']}")
print(f"Size: {blob['size']:,}")
# Create self-referential post
print("\nGenerating TID...")
tid = generate_tid()
print(f"TID: {tid}")
post_url = f"https://bsky.app/profile/{HANDLE}/post/{tid}"
print(f"Post URL: {post_url}")
post_text = f"""This is a self-referential post.
It contains its own record key ({tid})
It links to itself:
{post_url}
#atprotoing"""
# Calculate byte positions for the link
post_bytes = post_text.encode('utf-8')
url_start = post_bytes.find(post_url.encode('utf-8'))
url_end = url_start + len(post_url.encode('utf-8'))
record = {
"$type": "app.bsky.feed.post",
"text": post_text,
"createdAt": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"embed": {
"$type": "app.bsky.embed.external",
"external": {
"uri": "https://media.tenor.com/QXVs4QWLlzkAAAAC/spider-man.gif?hh=354&ww=498",
"thumb": blob,
"title": "Spider-Man pointing at Spider-Man",
"description": "ALT: Two Spider-Men pointing at each other (self-referential post!)"
}
},
"facets": [{
"index": {"byteStart": url_start, "byteEnd": url_end},
"features": [{"$type": "app.bsky.richtext.facet#link", "uri": post_url}]
}]
}
print("\nCreating post...")
post_response = requests.post(
"https://bsky.social/xrpc/com.atproto.repo.createRecord",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={
"repo": did,
"collection": "app.bsky.feed.post",
"rkey": tid,
"record": record
}
)
if post_response.status_code == 200:
print(f"\nSuccess!!!")
print(f"TID: {tid}")
print(f"Posted at: {post_url}")
else:
print(f"\nFailed to create post: {post_response.text}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment