move2link
AdsUrl bypass ambil final url dengan cepat, dan sangat lama jika solve manual ke web, dan menggunakan bypass cloudflare fgsi atau bisa manual menggunakan token SESSION di ambil manual
move2link.jsjavascript · 225 baris
#!/usr/bin/env node/** * Move2Link resolver * * Mode token: * node move2link.js --token "eyJ..." * node move2link.js --token-file token.txt * * Mode auto (Fgsi WAF Session): * node move2link.js --url "https://move2link.co/4b08f3c" * */import { readFileSync } from 'node:fs';import { stdin as input } from 'node:process';const API_BASE = 'https://api.move2link.com/api/v1';const JWT_RE = /^eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;const FGSI_KEY = 'AMBIL-KEY-DI-https://fgsi.dpdns.org';const FGSI_WAF_URL = 'https://fgsi.dpdns.org/api/tools/cfclearance/waf-session';function parseArgs() { const args = process.argv.slice(2); const get = (flag) => { const i = args.indexOf(flag); return i !== -1 && args[i + 1] ? args[i + 1] : null; }; return { url: get('--url') || args.find((a) => a.startsWith('http')) || null, token: get('--token'), cookie: get('--cookie'), tokenFile: get('--token-file'), stdin: args.includes('--stdin'), };}function asJwt(raw) { if (!raw) return null; let t = String(raw).trim(); t = t.replace(/^SESSION=/i, ''); t = t.replace(/^["']|["']$/g, ''); return JWT_RE.test(t) ? t : null;}function extractTokenFromCookie(cookie) { if (!cookie) return null; const m = String(cookie).match(/(?:^|;\s*)SESSION=([^;]+)/i); if (m) return asJwt(decodeURIComponent(m[1].trim())); return asJwt(cookie);}function jwtProgress(token) { try { const part = token.split('.')[1]; if (!part) return null; const pad = '='.repeat((4 - (part.length % 4)) % 4); const b64 = part.replace(/-/g, '+').replace(/_/g, '/') + pad; const json = Buffer.from(b64, 'base64').toString('utf8'); const p = JSON.parse(json); const step = Number(p.step); const maxStep = Number(p.max_step); if (!Number.isFinite(step) || !Number.isFinite(maxStep) || maxStep < 1) return null; return { step, maxStep }; } catch { return null; }}async function apiCall(path, method, token) { const endpoint = API_BASE + path; const res = await fetch(endpoint, { method, headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', Origin: 'https://move2link.co', Referer: 'https://move2link.co/', }, body: JSON.stringify({ token, csrf_token: 'x', imps: [] }), }); const text = await res.text(); let parsed = {}; try { parsed = JSON.parse(text); } catch {} if (!res.ok) { throw new Error(path + ' HTTP ' + res.status + ': ' + text.slice(0, 400)); } return parsed.data ?? parsed ?? {};}async function unlock(token) { let cur = token; const steps = []; for (;;) { const progress = jwtProgress(cur); if (!progress) throw new Error('JWT invalid / tidak ada step & max_step'); steps.push({ step: progress.step, maxStep: progress.maxStep }); if (progress.step >= progress.maxStep - 1) break; const data = await apiCall('/views/track', 'PUT', cur); const next = (data.token || '').trim(); if (!next || !JWT_RE.test(next)) { throw new Error('Track gagal. Response: ' + JSON.stringify(data).slice(0, 300)); } cur = next; } const data = await apiCall('/views/finalize', 'POST', cur); const finalUrl = String(data.redirect_url || data.url || '').trim(); if (!/^https?:\/\//i.test(finalUrl)) { throw new Error('Finalize gagal. Response: ' + JSON.stringify(data).slice(0, 400)); } return { finalUrl, steps };}/** Auto: Fgsi WAF Session → ambil SESSION JWT → unlock */async function resolveFromUrl(url) { const res = await fetch(`${FGSI_WAF_URL}?apikey=${FGSI_KEY}&url=${encodeURIComponent(url)}`); const json = await res.json(); if (!json.status || !json.data?.cookies) { throw new Error('Fgsi WAF Session gagal: ' + JSON.stringify(json).slice(0, 300)); } const sessionCookie = json.data.cookies.find((c) => c.name === 'SESSION'); if (!sessionCookie) { throw new Error('SESSION cookie tidak ada di response Fgsi'); } const token = asJwt(sessionCookie.value); if (!token) throw new Error('SESSION value bukan JWT valid'); return token;}async function readStdin() { const chunks = []; for await (const chunk of input) chunks.push(chunk); return Buffer.concat(chunks).toString('utf8').trim();}async function resolveToken(opts) { if (opts.token) { const t = asJwt(opts.token); if (t) return { token: t, source: 'token' }; } if (opts.cookie) { const t = extractTokenFromCookie(opts.cookie); if (t) return { token: t, source: 'cookie' }; } if (opts.tokenFile) { const raw = readFileSync(opts.tokenFile, 'utf8'); const t = asJwt(raw) || extractTokenFromCookie(raw); if (t) return { token: t, source: 'token-file' }; } if (opts.stdin) { const raw = await readStdin(); const t = asJwt(raw) || extractTokenFromCookie(raw); if (t) return { token: t, source: 'stdin' }; } if (opts.url) { const t = await resolveFromUrl(opts.url); return { token: t, source: 'fgsi+url' }; } return null;}async function main() { const opts = parseArgs(); const t0 = Date.now(); const out = { ok: false, finalUrl: null, durationMs: 0, durationSec: 0, steps: [], source: null, input: opts.url || null, timestamp: new Date().toISOString(), }; try { const resolved = await resolveToken(opts); if (!resolved) { throw new Error( 'Butuh --token / --token-file / --cookie / --stdin / --url. ' + 'Untuk auto full: node move2link.js --url https://move2link.co/xxxx' ); } out.source = resolved.source; const { finalUrl, steps } = await unlock(resolved.token); out.ok = true; out.finalUrl = finalUrl; out.steps = steps; } catch (err) { out.error = err.message || String(err); } const ms = Date.now() - t0; out.durationMs = ms; out.durationSec = Math.round((ms / 1000) * 100) / 100; if (!out.input) delete out.input; if (!out.error) delete out.error; if (!out.ok) { delete out.finalUrl; delete out.steps; } console.log(JSON.stringify(out, null, 2)); process.exit(out.ok ? 0 : 1);}main();README.mdmarkdown · 192 baris
Komentar
Masuk untuk ikut berkomentar.
Belum ada komentar.