XTOOLS / tools /hackernews.py
1990two's picture
Upload 31 files
0ee2df7 verified
Raw
History Blame Contribute Delete
2 kB
"""
HackerNews -- open Firebase API + Algolia search, no key.
"""
import httpx
FIREBASE_BASE = "https://hacker-news.firebaseio.com/v0"
ALGOLIA_BASE = "https://hn.algolia.com/api/v1/search"
def run(params: dict) -> dict:
mode = params.get("mode", "top")
limit = int(params.get("limit", 30))
if mode == "search":
query = params.get("query")
if not query:
raise ValueError("query is required for mode='search'")
min_points = int(params.get("min_points", 50))
resp = httpx.get(
ALGOLIA_BASE,
params={"query": query, "tags": "story", "numericFilters": f"points>{min_points}"},
timeout=15.0,
)
resp.raise_for_status()
hits = resp.json().get("hits", [])[:limit]
results = [
{
"title": h.get("title"),
"score": h.get("points"),
"comments": h.get("num_comments"),
"date": h.get("created_at"),
"url": h.get("url") or f"https://news.ycombinator.com/item?id={h.get('objectID')}",
}
for h in hits
]
return {"mode": "search", "count": len(results), "results": results}
# top stories snapshot
resp = httpx.get(f"{FIREBASE_BASE}/topstories.json", timeout=15.0)
resp.raise_for_status()
ids = resp.json()[:limit]
results = []
for story_id in ids:
item_resp = httpx.get(f"{FIREBASE_BASE}/item/{story_id}.json", timeout=10.0)
if item_resp.status_code != 200:
continue
item = item_resp.json()
results.append(
{
"title": item.get("title"),
"score": item.get("score"),
"comments": item.get("descendants"),
"author": item.get("by"),
"url": item.get("url") or f"https://news.ycombinator.com/item?id={story_id}",
}
)
return {"mode": "top", "count": len(results), "results": results}