UNPKG

cs2-inspect-lib

Version:

Enhanced CS2 Inspect URL library with full protobuf support, validation, and error handling

251 lines 9.91 kB
"use strict"; /** * Enhanced URL analysis and formatting utilities */ Object.defineProperty(exports, "__esModule", { value: true }); exports.requiresSteamClient = exports.normalizeInspectUrl = exports.isValidInspectUrl = exports.formatInspectUrl = exports.analyzeInspectUrl = exports.UrlAnalyzer = void 0; const types_1 = require("./types"); const errors_1 = require("./errors"); const validation_1 = require("./validation"); const INSPECT_BASE = "steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20"; /** * Enhanced URL analyzer with comprehensive validation */ class UrlAnalyzer { constructor(config = {}) { this.config = { ...types_1.DEFAULT_CONFIG, ...config }; } /** * Analyzes and parses an inspect URL */ analyzeInspectUrl(url) { if (this.config.validateInput) { const validation = validation_1.Validator.validateInspectUrl(url); if (!validation.valid) { throw new errors_1.InvalidUrlError(`URL validation failed: ${validation.errors.join(', ')}`, { errors: validation.errors, warnings: validation.warnings }); } } try { // Clean and normalize the URL let cleanedUrl = url.trim(); const originalUrl = url; // Handle various URL formats if (!cleanedUrl.startsWith("steam://")) { const previewVariants = [ "csgo_econ_action_preview ", "csgo_econ_action_preview%20", "+csgo_econ_action_preview ", "+csgo_econ_action_preview%20" ]; for (const variant of previewVariants) { if (cleanedUrl.startsWith(variant)) { cleanedUrl = INSPECT_BASE + cleanedUrl.slice(variant.length); break; } } // Handle raw data if (!cleanedUrl.startsWith("steam://")) { if (cleanedUrl.startsWith("M") || cleanedUrl.startsWith("S") || /^[0-9A-F]+$/i.test(cleanedUrl)) { cleanedUrl = INSPECT_BASE + cleanedUrl; } } } // Check if URL is quoted const isQuoted = cleanedUrl.includes("%20"); // Normalize to quoted format if (!isQuoted) { cleanedUrl = cleanedUrl.replace(/ /g, "%20"); } // Extract the payload const parts = cleanedUrl.split("csgo_econ_action_preview%20"); if (parts.length < 2) { throw new errors_1.InvalidUrlError('URL does not contain valid preview command', { url: originalUrl, cleanedUrl }); } const payload = parts[1]; if (!payload) { throw new errors_1.InvalidUrlError('URL payload is empty', { url: originalUrl, cleanedUrl }); } // Pattern for unmasked URLs (Market/Steam profile links) const unmaskedPattern = /^([SM])(\d+)A(\d+)D(\d+)$/; const unmaskedMatch = payload.match(unmaskedPattern); if (unmaskedMatch) { const [, typeChar, idValue, assetId, classId] = unmaskedMatch; // Validate numeric values if (!this.isValidId(idValue) || !this.isValidId(assetId) || !this.isValidId(classId)) { throw new errors_1.InvalidUrlError('Invalid numeric values in unmasked URL', { typeChar, idValue, assetId, classId }); } return { original_url: originalUrl, cleaned_url: cleanedUrl, url_type: 'unmasked', is_quoted: isQuoted, market_id: typeChar === 'M' ? idValue : undefined, owner_id: typeChar === 'S' ? idValue : undefined, asset_id: assetId, class_id: classId }; } // Pattern for masked URLs (hex-encoded protobuf data) const maskedPattern = /^[0-9A-Fa-f]+$/; const maskedMatch = payload.match(maskedPattern); if (maskedMatch) { // Validate hex data if (this.config.validateInput) { const hexValidation = validation_1.Validator.validateHexData(payload); if (!hexValidation.valid) { throw new errors_1.InvalidUrlError(`Invalid hex data: ${hexValidation.errors.join(', ')}`, { errors: hexValidation.errors }); } } return { original_url: originalUrl, cleaned_url: cleanedUrl, url_type: 'masked', is_quoted: isQuoted, hex_data: payload.toUpperCase() }; } throw new errors_1.InvalidUrlError('URL payload does not match any known format', { payload, expectedFormats: ['unmasked (M/S + numbers)', 'masked (hex data)'] }); } catch (error) { if (error instanceof errors_1.InvalidUrlError) { throw error; } throw new errors_1.InvalidUrlError('Failed to analyze inspect URL', { url, originalError: error }); } } /** * Formats an analyzed URL back to string format */ formatInspectUrl(urlInfo, options = {}) { const { quote = true, includeSteamPrefix = true } = options; try { let base; if (includeSteamPrefix) { if (quote) { // INSPECT_BASE already includes %20 base = INSPECT_BASE; } else { // Replace %20 with space for unquoted format base = INSPECT_BASE.replace('%20', ' '); } } else { const separator = quote ? "%20" : " "; base = "+csgo_econ_action_preview" + separator; } if (urlInfo.url_type === 'masked') { if (!urlInfo.hex_data) { throw new errors_1.InvalidUrlError('Masked URL missing hex data', { urlInfo }); } return base + urlInfo.hex_data; } else { const typeChar = urlInfo.market_id ? 'M' : 'S'; const idValue = urlInfo.market_id || urlInfo.owner_id; if (!idValue || !urlInfo.asset_id || !urlInfo.class_id) { throw new errors_1.InvalidUrlError('Unmasked URL missing required fields', { urlInfo }); } return `${base}${typeChar}${idValue}A${urlInfo.asset_id}D${urlInfo.class_id}`; } } catch (error) { if (error instanceof errors_1.InvalidUrlError) { throw error; } throw new errors_1.InvalidUrlError('Failed to format inspect URL', { urlInfo, options, originalError: error }); } } /** * Validates if a string represents a valid ID (numeric and reasonable length) */ isValidId(id) { if (!/^\d+$/.test(id)) { return false; } const num = parseInt(id, 10); return !isNaN(num) && num >= 0 && id.length <= 20; // Reasonable length limit } /** * Extracts basic information from URL without full decoding */ getUrlInfo(url) { try { const analyzed = this.analyzeInspectUrl(url); return { type: analyzed.url_type, isQuoted: analyzed.is_quoted, hasValidFormat: true, estimatedSize: analyzed.hex_data?.length || 0 }; } catch { return { type: 'invalid', isQuoted: url.includes('%20'), hasValidFormat: false }; } } /** * Checks if URL is likely a valid inspect URL without throwing errors */ isValidInspectUrl(url) { try { this.analyzeInspectUrl(url); return true; } catch { return false; } } /** * Normalizes URL format (converts to quoted Steam URL) */ normalizeUrl(url) { const analyzed = this.analyzeInspectUrl(url); return this.formatInspectUrl(analyzed, { quote: true, includeSteamPrefix: true }); } /** * Check if URL requires Steam client for inspection */ requiresSteamClient(url) { try { const analyzed = this.analyzeInspectUrl(url); return analyzed.url_type === 'unmasked'; } catch { return false; } } } exports.UrlAnalyzer = UrlAnalyzer; /** * Static convenience functions */ function analyzeInspectUrl(url, config) { const analyzer = new UrlAnalyzer(config); return analyzer.analyzeInspectUrl(url); } exports.analyzeInspectUrl = analyzeInspectUrl; function formatInspectUrl(urlInfo, options, config) { const analyzer = new UrlAnalyzer(config); return analyzer.formatInspectUrl(urlInfo, options); } exports.formatInspectUrl = formatInspectUrl; function isValidInspectUrl(url, config) { const analyzer = new UrlAnalyzer(config); return analyzer.isValidInspectUrl(url); } exports.isValidInspectUrl = isValidInspectUrl; function normalizeInspectUrl(url, config) { const analyzer = new UrlAnalyzer(config); return analyzer.normalizeUrl(url); } exports.normalizeInspectUrl = normalizeInspectUrl; function requiresSteamClient(url, config) { const analyzer = new UrlAnalyzer(config); return analyzer.requiresSteamClient(url); } exports.requiresSteamClient = requiresSteamClient; //# sourceMappingURL=url-analyzer.js.map