"""Project 109: who likes #gamedev posts? For a random sample of 60 posts, read public getLikes and count how many of the sampled posts each liker liked. An account that likes a large share of random posts is very likely automated (or a curator feed). Writes likers.json (handles only, counts).""" import json, random, time, urllib.request, urllib.parse from collections import Counter P = json.load(open("posts.json", encoding="utf-8"))["posts"] UA = {"User-Agent": "Vonk (AI character) counting like sources, project 109"} def get(path, **q): time.sleep(0.7) u = "https://public.api.bsky.app/xrpc/" + path + "?" + urllib.parse.urlencode(q) return json.load(urllib.request.urlopen(urllib.request.Request(u, headers=UA), timeout=25)) random.seed(1090) S = random.sample(P, 60) c = Counter(); bots = {}; per_post = [] for p in S: likes = []; cur = None for _ in range(3): q = {"uri": p["uri"], "limit": 100} if cur: q["cursor"] = cur r = get("app.bsky.feed.getLikes", **q) likes += r.get("likes", []); cur = r.get("cursor") if not cur: break hs = {l["actor"]["handle"] for l in likes} for l in likes: bots[l["actor"]["handle"]] = any(x.get("val") == "bot" for x in l["actor"].get("labels", [])) c.update(hs); per_post.append({"url": p["url"], "likes": p["likes"], "likers": sorted(hs)}) top = c.most_common(25) print("sampled posts", len(S), "unique likers", len(c)) for h, n in top: print(f"{n:3}/{len(S)} {h} bot-label={bots[h]}") json.dump({"n_posts": len(S), "top": top, "bot_label": {h: bots[h] for h, _ in top}, "per_post": per_post}, open("likers.json", "w", encoding="utf-8"), indent=1)