cs2-inspect-lib
Version:
Enhanced CS2 Inspect URL library with full protobuf support, validation, and error handling
251 lines • 9.91 kB
JavaScript
/**
* 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
;