/** * Article unlock strategies — find readable/archived versions of paywalled articles. * Three strategies tried in sequence: Wayback Machine, Google Cache, archive.ph. */ interface UnlockResult { success: boolean; strategy?: string; archiveUrl?: string; error?: string; } /** Try the Wayback Machine (web.archive.org). */ async function tryWaybackMachine(url: string): Promise { try { const apiUrl = `https://archive.org/wayback/available?url=${encodeURIComponent(url)}`; const res = await fetch(apiUrl, { signal: AbortSignal.timeout(10000) }); if (!res.ok) return { success: false }; const data = await res.json(); const snapshot = data?.archived_snapshots?.closest; if (snapshot?.available && snapshot.url) { return { success: true, strategy: "Wayback Machine", archiveUrl: snapshot.url.replace(/^http:/, "https:"), }; } return { success: false }; } catch { return { success: false }; } } /** Try Google Cache. */ async function tryGoogleCache(url: string): Promise { try { const cacheUrl = `https://webcache.googleusercontent.com/search?q=cache:${encodeURIComponent(url)}`; const res = await fetch(cacheUrl, { signal: AbortSignal.timeout(10000), redirect: "manual", }); // Google cache returns 200 if cached, redirects/errors otherwise if (res.status === 200) { return { success: true, strategy: "Google Cache", archiveUrl: cacheUrl, }; } return { success: false }; } catch { return { success: false }; } } /** Try archive.ph (archive.today). */ async function tryArchivePh(url: string): Promise { try { const checkUrl = `https://archive.ph/newest/${url}`; const res = await fetch(checkUrl, { signal: AbortSignal.timeout(10000), redirect: "manual", }); // archive.ph returns 302 redirect to the archived page if it exists if (res.status === 301 || res.status === 302) { const location = res.headers.get("location"); if (location) { return { success: true, strategy: "archive.ph", archiveUrl: location, }; } } // Sometimes it returns 200 directly with the archived content if (res.status === 200) { return { success: true, strategy: "archive.ph", archiveUrl: checkUrl, }; } return { success: false }; } catch { return { success: false }; } } /** * Try all unlock strategies in sequence. Returns the first successful result. */ export async function unlockArticle(url: string): Promise { // Validate URL try { new URL(url); } catch { return { success: false, error: "Invalid URL" }; } // Try strategies in order const strategies = [tryWaybackMachine, tryGoogleCache, tryArchivePh]; for (const strategy of strategies) { const result = await strategy(url); if (result.success) return result; } return { success: false, error: "No archived version found" }; }