import { createHash } from 'crypto'; import fetch from 'node-fetch'; // Try multiple community-maintained rule sources in order const RULES_URLS = [ 'https://raw.githubusercontent.com/rafa-9/dynamic-rules/main/rules.json', 'https://raw.githubusercontent.com/datawhores/onlyfans-dynamic-rules/main/dynamicRules.json', 'https://raw.githubusercontent.com/DATAHOARDERS/dynamic-rules/main/onlyfans.json', ]; const REFRESH_INTERVAL = 60 * 60 * 1000; // 1 hour let rules = null; let lastFetch = 0; function normalizeRules(raw) { // Different sources use different key names — normalize them return { static_param: raw.static_param, checksum_indexes: raw.checksum_indexes, checksum_constant: raw.checksum_constant ?? 0, checksum_constants: raw.checksum_constants ?? null, // per-index constants (some sources) app_token: raw.app_token || raw['app-token'] || '33d57ade8c02dbc5a333db99ff9ae26a', prefix: raw.prefix || raw.format?.split(':')[0], suffix: raw.suffix || raw.format?.split(':').pop(), remove_headers: raw.remove_headers ?? [], }; } async function fetchRules() { for (const url of RULES_URLS) { try { const res = await fetch(url); if (!res.ok) continue; const raw = await res.json(); rules = normalizeRules(raw); lastFetch = Date.now(); console.log(`[signing] Rules loaded from ${url} (prefix: ${rules.prefix})`); return rules; } catch (err) { console.warn(`[signing] Failed to fetch from ${url}: ${err.message}`); } } throw new Error('All dynamic rules sources failed'); } export async function initRules() { await fetchRules(); } export function getRules() { if (Date.now() - lastFetch > REFRESH_INTERVAL) { fetchRules().catch((err) => console.error('[signing] Failed to refresh rules:', err.message)); } return rules; } export function createSignedHeaders(path, userId) { if (!rules) throw new Error('Signing rules not initialized'); const timestamp = Date.now().toString(); // Use "0" for userId when user-id is in remove_headers const signUserId = rules.remove_headers?.includes('user-id') ? '0' : userId; const message = [rules.static_param, timestamp, path, signUserId].join('\n'); const sha1Hex = createHash('sha1').update(message).digest('hex'); const hexBytes = Buffer.from(sha1Hex, 'ascii'); let checksum = 0; for (let i = 0; i < rules.checksum_indexes.length; i++) { const byteVal = hexBytes[rules.checksum_indexes[i]]; const perIndex = rules.checksum_constants?.[i] ?? 0; checksum += byteVal + perIndex; } checksum += rules.checksum_constant; const sign = `${rules.prefix}:${sha1Hex}:${Math.abs(checksum).toString(16)}:${rules.suffix}`; return { sign, time: timestamp, 'app-token': rules.app_token, }; }