# Convert the stash sale list into the CSV that tools/traderie_lister.py expects. # # python scripts/make_traderie_csv.py # everything sellable # python scripts/make_traderie_csv.py --runes # runes only # # Writes traderie_items.csv, then: # python tools/traderie_lister.py --csv traderie_items.csv --dry-run import csv import os import re import sys _root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) os.chdir(_root) SRC = "stash_sale_list.csv" OUT = "traderie_items.csv" COLUMNS = ["name", "qty", "base_tier", "base_item", "ring_variant", "amulet_variant", "ethereal", "pricing", "price_amount", "price_currency"] # Runewords need a "Base Item" field. "No base" is the lister's own escape # hatch for when the socketed base does not matter to the listing. RUNEWORDS = {"spirit", "insight", "stealth", "lore", "smoke", "rhyme", "ancients pledge", "leaf", "malice", "steel", "zephyr", "king's grace", "edge", "harmony", "white", "peace", "myth", "black", "honor"} # Not tradeable / not worth a listing. Potions are consumables; sub-perfect gems # were already dropped upstream but are re-checked here so this script is safe to # run against any sale list. SKIP_WORDS = ("rejuvenation potion", "scroll of", "tome of", "key of", "chipped", "flawed", "flawless") def title_case(name: str) -> str: """Stash tooltips are ALL CAPS; Traderie searches title case. Apostrophes matter: "TAL RASHA'S" must become "Tal Rasha's", not "Tal Rasha'S", or the dropdown search misses. """ out = [] for word in name.lower().split(): if "'" in word: head, _, tail = word.partition("'") out.append(head.capitalize() + "'" + tail) else: out.append(word.capitalize()) return " ".join(out) def main(): runes_only = "--runes" in sys.argv with open(SRC, encoding="utf-8") as f: rows = list(csv.DictReader(f)) seen: dict[str, dict] = {} skipped = 0 for r in rows: raw = (r["name"] or "").strip() low = raw.lower() if not raw or any(w in low for w in SKIP_WORDS): skipped += 1 continue if runes_only and not low.endswith("rune"): continue name = title_case(raw) qty = r.get("qty") or "1" try: qty = int(qty) except ValueError: qty = 1 # The same item can sit in several cells (2x TARNHELM, 4x SMALL CHARM OF # INERTIA). Traderie wants one listing with a quantity, not N listings. if name in seen: seen[name]["qty"] += qty continue base_item = "No base" if name.lower() in RUNEWORDS else "" seen[name] = { "name": name, "qty": qty, "base_tier": "", "base_item": base_item, "ring_variant": "", "amulet_variant": "", "ethereal": "", # You said you would set prices yourself — ask_for_offers means the # listing goes up without committing to a number. "pricing": "ask_for_offers", "price_amount": "", "price_currency": "", } out = sorted(seen.values(), key=lambda r: (not r["name"].endswith("Rune"), r["name"])) with open(OUT, "w", newline="", encoding="utf-8") as f: w = csv.DictWriter(f, fieldnames=COLUMNS) w.writeheader() w.writerows(out) print(f"wrote {OUT}: {len(out)} listings ({skipped} skipped as untradeable)") runes = [r for r in out if r["name"].endswith("Rune")] print(f" runes: {len(runes)} other: {len(out) - len(runes)}") needs_base = [r["name"] for r in out if r["base_item"]] if needs_base: print(f" runewords given base_item='No base': {', '.join(needs_base)}") print("\nnext:") print(f" python tools/traderie_lister.py --csv {OUT} --dry-run") if __name__ == "__main__": main()