UNPKG

xclienttransaction

Version:

Generate Twitter/X Client-Transaction-ID header in JavaScript (Node & browser)

104 lines (93 loc) 3.07 kB
import { MIGRATION_REDIRECTION_REGEX, ON_DEMAND_FILE_REGEX, ON_DEMAND_FILE_URL } from './constants.js'; export class MathUtil { static round(num) { const x = Math.floor(num); if ((num - x) >= 0.5) { return Math.ceil(num); } return Math.sign(num) * x; } } export function generateHeaders() { return { 'Authority': 'x.com', 'Accept-Language': 'en-US,en;q=0.9', 'Cache-Control': 'no-cache', 'Referer': 'https://x.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'X-Twitter-Active-User': 'yes', 'X-Twitter-Client-Language': 'en' }; } export function validateResponse(response) { if (typeof response !== 'string') { throw new TypeError(`the response object must be string, not ${typeof response}`); } } export function getMigrationUrl(html) { const match = MIGRATION_REDIRECTION_REGEX.exec(html); return match ? match[0] : null; } export function getMigrationForm(html) { const formMatch = html.match(/<form[^>]*name=['"]f['"][^>]*>([\s\S]*?)<\/form>/); if (!formMatch) return null; const actionMatch = formMatch[0].match(/action=['"]([^'"]*)['"]/); const methodMatch = formMatch[0].match(/method=['"]([^'"]*)['"]/); const inputs = [...formMatch[0].matchAll(/<input[^>]*name=['"]([^'"]*)['"][^>]*value=['"]([^'"]*)['"]/g)]; const data = {}; for (const [, name, value] of inputs) { data[name] = value; } return { method: methodMatch ? methodMatch[1] : 'POST', url: actionMatch ? actionMatch[1] : 'https://x.com/x/migrate', data }; } export function getOndemandFileUrl(html) { const match = ON_DEMAND_FILE_REGEX.exec(html); if (!match) return null; return ON_DEMAND_FILE_URL.replace('{filename}', match[1]); } export function floatToHex(x) { let result = []; let quotient = Math.trunc(x); let fraction = x - quotient; let q = quotient; while (q > 0) { const newQ = Math.trunc(q / 16); const remainder = q - newQ * 16; result.unshift(remainder > 9 ? String.fromCharCode(remainder + 55) : String(remainder)); q = newQ; } if (fraction === 0) return result.join(''); result.push('.'); let f = fraction; while (f > 0) { f *= 16; const integer = Math.trunc(f); f -= integer; result.push(integer > 9 ? String.fromCharCode(integer + 55) : String(integer)); } return result.join(''); } export function isOdd(num) { return num % 2 ? -1.0 : 0.0; } export function base64Encode(input) { if (typeof Buffer !== 'undefined') { const buf = typeof input === 'string' ? Buffer.from(input) : Buffer.from(input); return buf.toString('base64'); } const bytes = typeof input === 'string' ? new TextEncoder().encode(input) : input; let binary = ''; for (const b of bytes) binary += String.fromCharCode(b); return btoa(binary); } export function base64Decode(input) { if (typeof Buffer !== 'undefined') { return Buffer.from(input, 'base64').toString(); } const binary = atob(input); return binary; }