#!/usr/bin/env node const crypto = require('crypto'); const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'; const FP_RAW = "webgl:ANGLE (Intel, Intel(R) UHD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)||audio:12.345678901234||canvas:abcdef123||fonts:20/25||system:8CPU,8GB,Win32,0,1,0||env:Asia/Jakarta,1920,1080,24,en-US||network:4g,10,50"; const sleep = ms => new Promise(r => setTimeout(r, ms)); const out = (data) => { console.log(JSON.stringify(data, null, 2)); process.exit(data.ok ? 0 : 1); }; // --- Cookie Jar --- class CookieJar { constructor() { this.jar = new Map(); } ingest(headers) { const cookies = typeof headers.getSetCookie === 'function' ? headers.getSetCookie() : [headers.get('set-cookie')].filter(Boolean); for (const s of cookies) { const [pair] = s.split(';'); const i = pair.indexOf('='); if (i > 0) this.jar.set(pair.slice(0, i).trim(), pair.slice(i + 1).trim()); } } header() { return [...this.jar.entries()].map(([k, v]) => `${k}=${v}`).join('; '); } get(k) { return this.jar.get(k); } } // --- Fetch with redirect control --- async function go(url, opts = {}, jar) { let cur = url, method = opts.method || 'GET', body = opts.body; const headers = { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.9', ...opts.headers }; for (let i = 0; i < 10; i++) { if (jar) { const c = jar.header(); if (c) headers['Cookie'] = c; } const res = await fetch(cur, { method, headers, body, redirect: 'manual', signal: AbortSignal.timeout(15000) }); if (jar) jar.ingest(res.headers); if (res.status >= 300 && res.status < 400) { const loc = res.headers.get('location'); if (!loc) return res; cur = new URL(loc, cur).href; method = 'GET'; body = undefined; delete headers['Content-Type']; delete headers['Content-Length']; headers['Referer'] = cur; continue; } Object.defineProperty(res, 'url', { value: cur }); return res; } } // --- Token generator --- function makeToken(xsrf) { const fp = crypto.createHash('sha256').update(FP_RAW).digest('hex'); const suffix = '#' + Buffer.from(fp).toString('base64'); return decodeURIComponent(xsrf).slice(0, 128 - suffix.length) + suffix; } // --- Safelink bypass --- async function bypass(url, html, jar) { const form = html.match(/