diff --git a/supabase/functions/runtime-opportunity-scout/algora-response-sample.json b/supabase/functions/runtime-opportunity-scout/algora-response-sample.json new file mode 100644 index 00000000000..17f23c522f9 --- /dev/null +++ b/supabase/functions/runtime-opportunity-scout/algora-response-sample.json @@ -0,0 +1,43 @@ +{ + "_note": "Sample Algora API response format (from https://api.docs.algora.io/bounties).", + "_url": "https://algora.io/api/bounties?status=open&limit=2", + "_note2": "The public API currently returns HTML (likely requires auth).", + "_note3": "This sample shows the documented response shape for reference.", + "next_cursor": "clcg36as9000bs8swc544ee8c", + "items": [ + { + "id": "clcg36as9000as8swwd5lmovp", + "forge": "github", + "repo_owner": "Uber4Coding", + "repo_name": "dev", + "number": 7, + "amount": 10000, + "currency": "USD", + "status": "active", + "created_at": "2023-01-03T10:27:05.626Z", + "updated_at": "2023-01-03T10:27:08.192Z", + "issue": { + "html_url": "https://github.com/Uber4Coding/dev/pull/7", + "title": "Update README.md", + "state": "open" + } + }, + { + "id": "clcg36as9000bs8swc544ee8d", + "forge": "github", + "repo_owner": "algora-io", + "repo_name": "tv", + "number": 42, + "amount": 5000, + "currency": "USD", + "status": "active", + "created_at": "2023-06-15T14:00:00.000Z", + "updated_at": "2023-06-15T14:00:00.000Z", + "issue": { + "html_url": "https://github.com/algora-io/tv/issues/42", + "title": "Implement tagging system for videos and channels", + "state": "open" + } + } + ] +} diff --git a/supabase/functions/runtime-opportunity-scout/index.ts b/supabase/functions/runtime-opportunity-scout/index.ts index 340b0928d4a..e125bd61ad9 100644 --- a/supabase/functions/runtime-opportunity-scout/index.ts +++ b/supabase/functions/runtime-opportunity-scout/index.ts @@ -200,23 +200,33 @@ async function fetchGithubBounties(): Promise { } // --- Source 3: Algora public bounties (best-effort, no key) --- -// If the public endpoint is unreachable or its shape changes, ignore cleanly. +// Uses the Algora public API endpoint. If the endpoint is unreachable or +// returns non-JSON (e.g. behind auth wall), the function returns [] cleanly. +// Expected response format (per https://api.docs.algora.io/bounties): +// { "next_cursor": "...", "items": [{ "id": "cl...", "amount": 10000, +// "currency": "USD", "status": "active", "issue": { "html_url": "...", +// "title": "..." }, ... }] } +// Amount is in cents (10000 = $100.00). async function fetchAlgora(): Promise { - const r = await fetchT("https://console.algora.io/api/bounties?status=open&limit=30", { - headers: { Accept: "application/json" }, + const r = await fetchT("https://algora.io/api/bounties?status=open&limit=50", { + headers: { Accept: "application/json, text/plain, */*" }, }); if (!r.ok) return []; const j = await r.json().catch(() => null); + if (!j) return []; // Tolerate both {items:[...]} and bare-array shapes. const items: Array> = Array.isArray(j) ? j : (j?.items as Array>) || (j?.bounties as Array>) || []; + return parseAlgoraItems(items); +} + +function parseAlgoraItems(items: Array>): Opportunity[] { const out: Opportunity[] = []; for (const it of items) { const url = String(it.url || it.html_url || it.link || ""); if (!url || !/^https?:\/\//.test(url)) continue; const title = String(it.title || it.task || it.name || "").slice(0, 200); - // Algora amounts are typically minor units (cents) under reward/amount. const rewardObj = (it.reward as Record | null) || (it.amount as Record | null) || null; let reward: number | null = null; if (rewardObj && typeof rewardObj === "object" && "amount" in rewardObj) {