# -*- coding: utf-8 -*- """取证脚本(第 2 棒专用):为「Postiz + PostSider 同源双形态分析」补拉增量面证据。 只读 GitHub 公开 raw / API;产物落本目录 api/fork/ 下,逐件带取证时间。 ⛔ 不重复拉第 1 棒已有的 repo.json / README / tree(那些直接复用 api/ 下的)。 """ import json, os, sys, time, urllib.request, urllib.error sys.stdout.reconfigure(encoding="utf-8", errors="replace") HERE = os.path.dirname(os.path.abspath(__file__)) OUT = os.path.join(HERE, "api", "fork") os.makedirs(OUT, exist_ok=True) HDRS = {"User-Agent": "wb-research/1.0"} # (落盘名, raw 路径, 仓库) RAW = "https://raw.githubusercontent.com/%s/%s/%s" # owner/repo/branch/path ITEMS = [ ("postsider.apps_mcp.README.md", "lumizone/postsider", "main", "apps/mcp/README.md"), ("postsider.apps_mcp.server.json", "lumizone/postsider", "main", "apps/mcp/server.json"), ("postsider.apps_mcp.package.json", "lumizone/postsider", "main", "apps/mcp/package.json"), ("postsider.apps_mcp.CHANGELOG.md", "lumizone/postsider", "main", "apps/mcp/CHANGELOG.md"), ("postsider.mcp.tool-inventory.ts", "lumizone/postsider", "main", "apps/mcp/src/__tests__/tool-inventory.ts"), ("postsider.mcp.server.ts", "lumizone/postsider", "main", "apps/mcp/src/server.ts"), ("postsider.sdk.README.md", "lumizone/postsider", "main", "apps/sdk/README.md"), ("postsider.sdk.index.ts", "lumizone/postsider", "main", "apps/sdk/src/index.ts"), ("postsider.sdk.package.json", "lumizone/postsider", "main", "apps/sdk/package.json"), ("postsider.package.json", "lumizone/postsider", "main", "package.json"), ("postsider.ATTRIBUTION.md", "lumizone/postsider", "main", "ATTRIBUTION.md"), ("postsider.DEPLOYMENT.md", "lumizone/postsider", "main", "DEPLOYMENT.md"), ("postsider.skill.calendar-review.md", "lumizone/postsider", "main", "apps/mcp/skills/postsider-calendar-review/SKILL.md"), ("postsider.skill.content-publishing.md", "lumizone/postsider", "main", "apps/mcp/skills/postsider-content-publishing/SKILL.md"), ("postiz.package.json", "gitroomhq/postiz-app", "main", "package.json"), ("postiz.version.txt", "gitroomhq/postiz-app", "main", "version.txt"), ("postiz.sdk.README.md", "gitroomhq/postiz-app", "main", "apps/sdk/README.md"), ("postiz.sdk.package.json", "gitroomhq/postiz-app", "main", "apps/sdk/package.json"), ] log = {"fetched_at": time.strftime("%Y-%m-%dT%H:%M:%S"), "items": {}} for name, repo, branch, path in ITEMS: url = RAW % (repo, branch, path) rec = {"repo": repo, "branch": branch, "path": path, "url": url} try: req = urllib.request.Request(url, headers=HDRS) with urllib.request.urlopen(req, timeout=40) as r: body = r.read() with open(os.path.join(OUT, name), "wb") as f: f.write(body) rec["bytes"] = len(body) rec["ok"] = True except urllib.error.HTTPError as e: rec["ok"] = False rec["error"] = "HTTP %s" % e.code except Exception as e: rec["ok"] = False rec["error"] = repr(e) log["items"][name] = rec print("[%s] %s" % ("OK " if rec.get("ok") else "FAIL", name), rec.get("error", rec.get("bytes"))) time.sleep(0.3) # 版本差异:取两个仓库的 releases(最近 10 条)与 tags def api(url): req = urllib.request.Request(url, headers={**HDRS, "Accept": "application/vnd.github+json"}) with urllib.request.urlopen(req, timeout=40) as r: return json.loads(r.read().decode("utf-8")) for slug, full in (("postiz", "gitroomhq/postiz-app"), ("postsider", "lumizone/postsider")): try: rl = api("https://api.github.com/repos/%s/releases?per_page=10" % full) save = [{"tag_name": x.get("tag_name"), "name": x.get("name"), "published_at": x.get("published_at"), "prerelease": x.get("prerelease")} for x in rl] with open(os.path.join(OUT, slug + ".releases.json"), "w", encoding="utf-8") as f: json.dump(save, f, ensure_ascii=False, indent=1) log["items"][slug + ".releases"] = {"ok": True, "count": len(save)} print("[releases]", slug, len(save)) except Exception as e: log["items"][slug + ".releases"] = {"ok": False, "error": repr(e)} print("[releases]", slug, "FAIL", repr(e)) with open(os.path.join(OUT, "_fetch_log.json"), "w", encoding="utf-8") as f: json.dump(log, f, ensure_ascii=False, indent=1) print("\nDONE -> " + OUT)