Brings up the pull-model scraper: the .NET C2 hands skin+wear jobs to Python nodriver workers that scrape cs.money and post results back, plus the supporting Core/EFCore data model, migrations, and docker-compose orchestration. IPRoyal proxying lets workers scale horizontally with a distinct residential exit IP each: every worker process mints its own sticky session at startup, and an in-process forwarding proxy injects the gateway auth so Chromium talks only to an auth-free localhost endpoint (zero CDP). On a Cloudflare challenge a worker rotates to a fresh session/IP and re-warms. Verified end-to-end against live IPRoyal: distinct US residential exits per worker and IP rotation on demand. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.4 KiB
PL/PgSQL
64 lines
2.4 KiB
PL/PgSQL
-- ============================================================
|
|
-- CS2 Skin Tracker — find_listings()
|
|
-- Run against the skintracker database as the app role (owner)
|
|
-- to (re)create the function. Safe to re-run (CREATE OR REPLACE).
|
|
--
|
|
-- Purpose: look up active listings for a specific skin by weapon
|
|
-- name + finish name, with an optional wear filter.
|
|
--
|
|
-- Examples:
|
|
-- SELECT * FROM skintracker.find_listings('AK-47', 'Blue Laminate');
|
|
-- SELECT * FROM skintracker.find_listings('m4a1-s', 'Player Two', 'ft');
|
|
-- SELECT cs_float_listing_id, price, wear_name, float_value, is_stat_trak
|
|
-- FROM skintracker.find_listings('M4A4', 'Howl', 'mw');
|
|
-- ============================================================
|
|
|
|
SET search_path = skintracker;
|
|
|
|
CREATE OR REPLACE FUNCTION skintracker.find_listings(
|
|
p_weapon text, -- e.g. 'AK-47', 'M4A4', 'M4A1-S' (case-insensitive)
|
|
p_skin text, -- e.g. 'Blue Laminate', 'Electric Blue' (case-insensitive)
|
|
p_wear text DEFAULT NULL -- optional: fn | mw | ft | ww | bs (case-insensitive)
|
|
)
|
|
RETURNS SETOF skintracker.listings
|
|
LANGUAGE plpgsql
|
|
STABLE
|
|
AS $$
|
|
DECLARE
|
|
v_wear_name text;
|
|
BEGIN
|
|
-- Map the optional wear abbreviation to the full wear name CSFloat reports.
|
|
-- NULL / blank means "any wear". An unrecognised value is an error rather than
|
|
-- a silent empty result.
|
|
IF p_wear IS NOT NULL AND btrim(p_wear) <> '' THEN
|
|
v_wear_name := CASE lower(btrim(p_wear))
|
|
WHEN 'fn' THEN 'Factory New'
|
|
WHEN 'mw' THEN 'Minimal Wear'
|
|
WHEN 'ft' THEN 'Field-Tested'
|
|
WHEN 'ww' THEN 'Well-Worn'
|
|
WHEN 'bs' THEN 'Battle-Scarred'
|
|
ELSE NULL
|
|
END;
|
|
|
|
IF v_wear_name IS NULL THEN
|
|
RAISE EXCEPTION 'Unknown wear abbreviation: "%". Use one of: fn, mw, ft, ww, bs.', p_wear;
|
|
END IF;
|
|
END IF;
|
|
|
|
RETURN QUERY
|
|
SELECT l.*
|
|
FROM skintracker.listings l
|
|
JOIN skintracker.skins s ON s.id = l.skin_id
|
|
JOIN skintracker.weapons w ON w.id = s.weapon_id
|
|
WHERE l.status = 'Active'
|
|
AND lower(w.name) = lower(btrim(p_weapon))
|
|
AND lower(s.name) = lower(btrim(p_skin))
|
|
AND (v_wear_name IS NULL OR l.wear_name = v_wear_name)
|
|
ORDER BY l.price ASC, l.float_value ASC;
|
|
END;
|
|
$$;
|
|
|
|
-- If you use the optional read-only reporting role (see 02_readonly_role.sql),
|
|
-- let it call the function:
|
|
-- GRANT EXECUTE ON FUNCTION skintracker.find_listings(text, text, text) TO skintracker_readonly;
|