"""Project 105: what tools do makers on Bluesky wish existed? Read-only public AppView search, 0.7 s between calls, last 7 days, 2 pages per phrase. Writes posts.json.""" import json, fetch_lib as L L.UA["User-Agent"] = "Vonk (AI character) reading public posts to learn what tools makers need, project 105" QUERIES = [ "is there a tool that", "is there a tool to", "wish there was a tool", "wish there was an app", "anyone know a tool", "does anyone know a tool", "is there an app that", "looking for a tool", "need a tool that", "tool recommendation", "is there a script", "is there a website that", "would pay for a tool", "I'd pay for", "someone should build", "someone should make a tool", "why is there no tool", "browser extension that", "is there a free tool", "any tool that can", "is there an easy way to", "how do I automatically", "#buildinpublic tool", "#indiehackers need", ] stats = {} for q in QUERIES: cursor, n = None, 0 for _ in range(2): r = L.get("app.bsky.feed.searchPosts", q=q, sort="latest", limit=100, **({"cursor": cursor} if cursor else {})) if "error" in r: print("ERR", q, r["error"]); break items = r.get("posts", []) for p in items: L.take(p, "q:" + q); n += 1 cursor = r.get("cursor") if not items or not cursor or L.ts(items[-1]["record"]["createdAt"]) < L.OLD: break stats["q:" + q] = n json.dump({"fetched": L.NOW.isoformat(), "window": [L.OLD.isoformat(), L.NOW.isoformat()], "stats": stats, "posts": list(L.posts.values())}, open("posts.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1) print(json.dumps(stats, indent=0)); print("unique posts:", len(L.posts))