using System.Text; namespace BlueLaminate.Core.SkinLand; /// /// Builds a skin.land market URL from the catalogue's weapon + skin + wear. skin.land's /// market routes are /market/csgo/{slug}/ where the slug is simply /// {weapon}-{skin}-{wear} kebab-cased — verified against the live site (e.g. /// "M4A4" + "Global Offensive" + "Battle-Scarred" → m4a4-global-offensive-battle-scarred, /// "AK-47" + "Redline" + "Field-Tested" → ak-47-redline-field-tested). No discovery /// or stored mapping is needed. /// /// StatTrak and Souvenir are separate pages on skin.land (stattrak-/ /// souvenir- prefixed slugs); this builds the base (non-special) page, which is the /// unit v1 sweeps per SkinCondition. /// /// public static class SkinLandSlug { private const string MarketBase = "https://skin.land/market/csgo/"; /// "M4A4", "Global Offensive", "Battle-Scarred" → the full market URL. public static string MarketUrl(string weapon, string skinName, string condition) => $"{MarketBase}{Slugify($"{weapon} {skinName} {condition}")}/"; /// /// Lowercase, collapse every run of non-alphanumeric characters to a single hyphen, /// and trim leading/trailing hyphens. So "AK-47 | Redline (Field-Tested)" and the /// catalogue's "AK-47 Redline Field-Tested" both reduce to "ak-47-redline-field-tested". /// public static string Slugify(string value) { var sb = new StringBuilder(value.Length); var pendingHyphen = false; foreach (var ch in value) { if (char.IsLetterOrDigit(ch)) { if (pendingHyphen && sb.Length > 0) { sb.Append('-'); } sb.Append(char.ToLowerInvariant(ch)); pendingHyphen = false; } else { pendingHyphen = true; } } return sb.ToString(); } }