SkinScope
Explore CS2 skin prices from the Steam Market in one Discord embed.
Commands this plugin adds
About this plugin
SkinScope
CS2 skin price lookup for the YourBot.gg Discord bot platform (formerly branded MMO Maid; the platform rename shipped with SDK 0.6.0).
/skin query: ak redline
Returns a rich Discord embed with the skin's image, rarity tier, valid wears,
and Steam Market + Skinport + CSFloat prices side by side (USD, from
build-time bundled dumps) — each non-Steam field shows its lowest ask, a
vs Steam delta, and the listing count. The embed title is a clickable hyperlink to the
Steam Market listings page — the buy path. Interactive wear and quality buttons
let users explore variants, editing the card in place.
Features
- Three price sources, side by side — Steam Market (crawled directly
from the Community Market's
search/renderendpoint), Skinport (public/v1/itemsAPI) and CSFloat (public/v1/listings/price-list), all sourced at build time. Each non-Steam field carries a−X.X% vs Steamdelta and listing count, so the card is a real cross-market comparison — and the two independent markets are usually the cheaper places to buy. Refresh withtools/build_steam.py+tools/build_skinport.py+tools/build_csfloat.py— see "Refreshing prices" below. - Cheapest-market headline — the line under the card title names the
lowest-priced market for that exact variant and how far under Steam it
sits (
**Cheapest: CSFloat $6.00** · −40.0% vs Steam). Shown only when at least two markets priced it; a single price is not a comparison. - Honest deltas — a
vs Steamfigure computed against a dump refreshed weeks apart is prefixed~, because it measures elapsed time as much as market spread. Each field also states its own age ("priced 3d ago"). - Clickable embed title — links straight to the Steam Market listings page for the exact (skin, wear, quality) variant. One-click buy path.
- In-place card editing — wear/quality/jump buttons update the existing card (Discord UPDATE_MESSAGE, SDK 0.7.0+) instead of posting a new message.
- Your search stays on the card — the bottom row hops between the other
matches from your query (and back to the one you came from). The whole result
set rides in each button's
custom_id, so it survives every click instead of disappearing after the first one. - One-click onboarding — a bare
/skinshows a numbered quick-start with a "Try an example" button that renders a live result in one click. - Smart filters: category (rifle/SMG/pistol/sniper/knife/glove/agent), rarity tier (Consumer → Covert → Knife/Glove), wear (FN/MW/FT/WW/BS), StatTrak/Souvenir/Normal
- Browse mode:
/skin category: knife(no query) shows top items in that category, each reachable from the jump row - Admin dashboard (YourBot panel → plugin → Overview): lookup volume, bundled-data health (every source, with each dump's provenance and age), error-code table — no Discord commands needed
- 2155-item catalog (2092 skins + 63 agents) bundled in the plugin, no external DB needed
- Zero runtime HTTP — all three price dumps and the catalog ship in the zip; no allowlist setup needed
- Sandbox-safe — fits in the platform's 64MB RAM envelope; no egress required
Why bundled-only
The v0.1.x design fetched prices live from Steam Market and DMarket. The v0.2.1 design added live SkinBaron alongside the bundled Steam dump. Both live-fetch directions failed in production for the same reason: MMO Maid's sandbox proxy egress IPs are TCP-reset by every Cloudflare-fronted skin marketplace during TLS handshake (Steam, DMarket, and SkinBaron all honour the same shared blocklist). The connection dies below the HTTP layer, so no in-plugin mitigation works.
v0.2.0 sidestepped Steam's block by baking ByMykel's CS2 price tracker
data into the zip. v0.7.0 applies the same lesson to restore the
comparative pricing the plugin was built around: Skinport is also
Cloudflare-fronted (a live fetch would hit the same wall), but its /v1/items
API is reachable from the build host, so a second bundled dump gives a genuine
Steam-vs-Skinport comparison with zero runtime HTTP. See
ARCHITECTURE.md and
SDK-ASSUMPTIONS.md for the rationale and
CHANGELOG.md v0.7.0 for how Skinport was chosen (CSGOTrader's
old aggregate feed is dead; the choice was made by live-probing, not memory).
Quickstart
# Build the catalog from byMykel/CSGO-API (run when refreshing for new Valve content)
py tools/build_catalog.py
# Build the bundled price dumps (Steam crawled direct, Skinport from /v1/items)
py tools/build_steam.py # long-running: a full crawl is ~2,200 pages, resumable
py -m pip install brotli # one-time: Skinport's API only serves Brotli (build host only)
py tools/build_skinport.py
py tools/build_csfloat.py # single unauthenticated GET, seconds
# Run the full test suite
py -m pytest tests/ -q
# Run all 9 audit gates
py tools/run_audit.py
# Produce the shippable plugin zip
py tools/build_bundle.py
# -> dist/skinscope.zip
# Run the platform's own upload validator against the artifact
py tools/validate_artifact.py
Refreshing prices
Both bundled dumps are point-in-time. Re-run the builds when prices feel stale:
py tools/build_steam.py # Steam: direct Community Market crawl → data/prices.json.gz
py tools/build_skinport.py # Skinport: fetches /v1/items (needs brotli) → data/skinport.json.gz
py tools/build_csfloat.py # CSFloat: one GET of /v1/listings/price-list → data/csfloat.json.gz
py tools/build_bundle.py # repackages dist/skinscope.zip
# upload dist/skinscope.zip on the YourBot dev portal (auto-deploy on push if wired up)
The Steam crawl walks ~2,200 pages under the politeness budget recorded in
docs/SOURCES-LEGAL.md (≤100 req/5min, 3s inter-page),
so a full rebuild takes hours. It checkpoints into .build-cache/ and resumes,
so an interrupted run continues instead of restarting; --max-pages N runs a
bounded smoke crawl that confirms the live response shape without writing the
dump. Skinport and CSFloat are each a single API call. Every builder gates on catalog
coverage and exits non-zero on a partial result. The three dumps are
independent — refresh one without the others if you like, and a dump that
fails to load at runtime degrades only its own field on the card.
Whatever is currently bundled stamps its own provenance into the dump, and
/skin-config plus the dashboard's Data health widget report it at runtime —
read it there rather than inferring it from this README.
Deploy
No HTTP Domains allowlist setup is required — v0.2.2 has zero runtime HTTP.
Drop the bundle into the YourBot plugin manager (or push to GitHub if
auto-deploy is wired up) and confirm the two Safe-tier capabilities
(interaction:respond, storage:kv) when prompted. Run
py tools/validate_artifact.py first — it's the same validator the
upload pipeline runs.
Repository layout
manifest.json YourBot plugin manifest
dashboard_manifest.json admin dashboard (generated by tools/build_dashboard.py)
__main__.py platform entry point — imports plugin.py, calls plugin.run()
plugin.py slash handlers + component dispatcher (importable for tests)
requirements.txt empty (no pip deps — stdlib only)
lib/
catalog.py load + search bundled compact catalog (inverted token index)
pricing.py Steam + Skinport lookup via bundled price dumps; SOURCE_META feature matrix
links.py Steam Market deep-link URL builders (clickable embed title)
embed.py Discord embed + button-row builders (two side-by-side source fields)
state.py wear/quality enums, custom_id encode/decode (versioned)
scoring.py pure-Python fuzzy search scorer
config.py per-server currency table (USD-only surface in v0.2)
reasons.py negative-cache reason-code constants + user-facing hints
logctx.py per-request correlation id (ContextVar)
metricq.py request-scoped deferred-metrics queue (3s-deadline guard)
data/
catalog.json.gz ~190 KB compact CS2 catalog (regenerated by tools/build_catalog.py)
prices.json.gz ~97 KB Steam Market prices (regenerated by tools/build_steam.py)
skinport.json.gz ~142 KB Skinport prices (regenerated by tools/build_skinport.py)
csfloat.json.gz ~122 KB CSFloat prices (regenerated by tools/build_csfloat.py)
tools/ offline build + audit + bundle scripts (not shipped in zip)
tests/ pytest suite (not shipped in zip)
Documentation
| File | Purpose |
|---|---|
| CLAUDE.md | Handoff doc — what a contributor needs to know first |
| ARCHITECTURE.md | The "why" doc — design decisions and rationale |
| reference.md | SDK contract transcription (v0.5.x — superseded by the installed yourbot_sdk source on any conflict) |
| AUDIT-REPORT.md | Hardening audits, all findings + status |
| RUNBOOK.md | Operational scenarios + recovery procedures |
| CHANGELOG.md | Versioned change log |
| SDK-ASSUMPTIONS.md | Confirmed SDK-behavior assumptions discovered in production |
Plugin spec at a glance
- SDK: yourbot-sdk 0.10.0 installed (latest on PyPI; renamed from
mmo_maid_sdkin 0.6.0; the installed source is authoritative over any doc) - Capabilities:
interaction:respond,storage:kv(Safe tier, no install warning) - Python: 3.11+
- External APIs at runtime: none. All three price dumps and the catalog ship in the zip.
- Catalog source: byMykel/CSGO-API
- Steam prices source: Steam Community Market
search/render, crawled directly bytools/build_steam.py(posture: docs/SOURCES-LEGAL.md) - Skinport prices source: Skinport
/v1/itemsAPI (build-time only; needsbrotli) - CSFloat prices source: CSFloat
/api/v1/listings/price-list(build-time only; unauthenticated — note that/api/v1/listings, which the vendor docs point at, requires a login: see docs/SOURCES-LEGAL.md) - Audit gates: 9 / 9 passing (
py tools/run_audit.py)
Status
v0.11.4 is the current shipping release: three bundled price sources (Steam Market, Skinport, CSFloat) side by side, a jump row that carries your whole result set so it survives every click, in-place card editing, and one-click onboarding. 432 tests, 9/9 audit gates, and a clean pass from the platform's own artifact validator.
v0.1.x's hardening (114 audit findings across three rounds — concurrency races, adversarial Unicode, partial upstream outages, cache-stampede thundering herds, SDK contract drift) is preserved where applicable; the pieces that became dead code after the bundled-only pivot were removed cleanly. See CHANGELOG.md for the full per-release history.
Steam prices are first-party as of v0.11.3: the bundled dump is stamped
steam-market-direct — a full crawl of the Community Market's search/render
endpoint by tools/build_steam.py (14,511 priced catalog entries,
walked 2026-09-11/12). The ByMykel aggregator is gone from the Steam price path.
All three dumps now date from the same day, so vs Steam deltas render
without the ~ approximate marker. /skin-config reports each dump's real
provenance and age.
License
TBD.
Related plugins
Have a question?
Ask the developer of SkinScope directly. Sign in with Discord to send your question. You'll be notified when they reply.
Sign in to ask the developer →Ready to install SkinScope?
Sign in with Discord, set up your server and add it in minutes.
Install on your server →