// Thin client for the HTTP API in EVENTS.md. export type PostListResult = | { ok: true; listId: string } | { ok: false; pranked: true; message: string; cooldownS: number } | { ok: false; pranked: false; message: string }; export async function postList(text: string, requester: string, nestId: string): Promise { const res = await fetch("/api/lists", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text, requester, nest_id: nestId }), }); const body = await res.json().catch(() => ({})); if (res.ok) return { ok: true, listId: String(body.list_id) }; if (res.status === 429) return { ok: false, pranked: true, message: String(body.message ?? "Caw."), cooldownS: Number(body.cooldown_s ?? 30) }; return { ok: false, pranked: false, message: String(body.message ?? `The flock said no (${res.status}).`) }; } export async function postVerdict(itemId: string, target: "triage" | "output", approve: boolean): Promise { const res = await fetch("/api/verdicts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ item_id: itemId, target, approve }), }); if (!res.ok) throw new Error(res.status === 404 ? "That question already has an answer." : `Verdict failed (${res.status}).`); } export type TrustLevels = Map; // "Name|task_type" -> level export async function getTrust(): Promise { const out: TrustLevels = new Map(); try { const res = await fetch("/api/trust"); const body = await res.json(); for (const [key, rec] of Object.entries<{ level?: number }>(body.crows ?? {})) out.set(key, rec.level ?? 0); } catch { // Badges start at zero if the ledger can't be read. } return out; } export function listEvents(listId: string): EventSource { return new EventSource(`/api/events?list_id=${encodeURIComponent(listId)}`); } // ---------- nests ---------- export type Nest = { nestId: string; name: string; crows: string[] }; export type Nests = { roster: string[]; nests: Nest[] }; const DEFAULT_ROSTER = ["Hugin", "Munin", "Corvina", "Soot", "Pepper", "Inky", "Edgar", "Jet"]; export async function getNests(): Promise { try { const res = await fetch("/api/nests"); if (!res.ok) throw new Error(String(res.status)); const body = await res.json(); return { roster: (body.roster ?? DEFAULT_ROSTER).map(String), nests: (body.nests ?? []).map((n: any) => ({ nestId: String(n.nest_id), name: String(n.name), crows: (n.crows ?? []).map(String) })), }; } catch { // A server without nests still has the default room. return { roster: DEFAULT_ROSTER, nests: [{ nestId: "todo", name: "todo", crows: [] }] }; } } export async function createNest(name: string, crows: string[]): Promise { const res = await fetch("/api/nests", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, crows }), }); const body = await res.json().catch(() => ({})); if (!res.ok) throw new Error(String(body.message ?? `Couldn't build that nest (${res.status}).`)); return { nestId: String(body.nest_id), name: String(body.name), crows: (body.crows ?? []).map(String) }; }