"""
NationStates High-Value Trading Card Tracker (CLI)
===================================================
Runs a full scan pipeline using the NationStates API directly.
1. Scans cards by ID range for Legendary/Epic (Low ID filter)
2. Checks if flagged nations are dead (CTE filter)
3. Matches against historical names
4. Checks live market prices
5. Generates dashboard.html
COMPLIANCE: This script NEVER executes automated trades.
All purchases are manual via generated URL links.
Rate limit: 1.2s between every API request.
Usage: python tracker.py [--max-id 10000] [--price-checks 30]
"""
import sys
import os
import time
import sqlite3
from datetime import datetime
# Add script directory to path for imports
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPT_DIR)
from core import (
init_db, scan_low_id_cards, scan_cte_cards, scan_historical_cards,
check_target_prices, SEASON, RATE_LIMIT_SECONDS, THRESHOLDS
)
DB_PATH = os.path.join(SCRIPT_DIR, "cards.db")
DASHBOARD_PATH = os.path.join(SCRIPT_DIR, "dashboard.html")
# Defaults (can be overridden via command line)
DEFAULT_MAX_ID = 10000
DEFAULT_PRICE_CHECKS = 30
# ==========================================
# CLI CALLBACKS
# ==========================================
def scan_callback(status, card_id, card):
if status == "found":
print(f" ★ ID:{card_id} — {card['name']} ({card['rarity']}) MV:{card['market_value']}")
else:
name = card["name"] if card else "not found"
rarity = card["rarity"] if card else ""
if card:
print(f" · ID:{card_id} — {name} ({rarity}) skip")
else:
print(f" · ID:{card_id} — not found")
def cte_callback(status, card_id, name):
if status == "cte":
print(f" 💀 {name} (ID:{card_id}) — DEAD")
else:
print(f" ✓ {name} (ID:{card_id}) — alive")
# ==========================================
# DASHBOARD GENERATION
# ==========================================
def generate_dashboard(alerts):
"""Generate HTML dashboard."""
print("\n[HTML] Generating dashboard...")
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute("SELECT COUNT(*) FROM cards")
total_cards = c.fetchone()[0]
c.execute("SELECT COUNT(*) FROM targets")
total_targets = c.fetchone()[0]
c.execute("""
SELECT card_id, name, rarity, flag_reason, lowest_ask, last_checked, card_url
FROM targets ORDER BY
CASE rarity WHEN 'legendary' THEN 1 WHEN 'epic' THEN 2 ELSE 3 END, lowest_ask ASC
""")
all_targets = c.fetchall()
conn.close()
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
rarity_colors = {
"legendary": "#FFD700", "epic": "#FF9800", "ultra-rare": "#9C27B0",
"rare": "#2196F3", "uncommon": "#4CAF50", "common": "#8B8B8B"
}
alerts_html = ""
if alerts:
alerts_html = f'
🚨 {len(alerts)} Undervalued Cards!
'
for a in alerts:
color = rarity_colors.get(a["rarity"], "#fff")
alerts_html += (
f'
{a["rarity"].upper()} '
f'
{a["name"]} (ID:{a["card_id"]})
'
f'Reason: {a["reason"]}
'
f'Ask:
{a["ask"]:.2f} (threshold: {a["threshold"]})
'
f'
🛒 View / Buy '
)
alerts_html += "
"
else:
alerts_html = '✅ No undervalued cards below threshold.
'
table_rows = ""
for row in all_targets:
card_id, name, rarity, flag_reason, lowest_ask, last_checked, card_url = row
color = rarity_colors.get(rarity, "#fff")
ask_display = f"{lowest_ask:.2f}" if lowest_ask is not None else "—"
checked_display = last_checked[:16] if last_checked else "—"
table_rows += (
f'| {card_id} | {name} | '
f'{rarity} | '
f'{flag_reason} | {ask_display} | '
f'{checked_display} | '
f'View |
'
)
html = f"""
NS Card Tracker
🏆 NS High-Value Card Tracker
Generated: {now} | Season {SEASON} | API-only (no dumps)
{total_cards}
Cards Scanned
{alerts_html}
📋 All Targets
| ID | Name | Rarity | Reason | Ask | Checked | Link |
{table_rows}
"""
with open(DASHBOARD_PATH, "w", encoding="utf-8") as f:
f.write(html)
print(f"[HTML] Saved: {DASHBOARD_PATH}")
# ==========================================
# MAIN
# ==========================================
def main():
max_id = DEFAULT_MAX_ID
price_checks = DEFAULT_PRICE_CHECKS
for i, arg in enumerate(sys.argv[1:]):
if arg == "--max-id" and i + 2 <= len(sys.argv[1:]):
max_id = int(sys.argv[i + 2])
if arg == "--price-checks" and i + 2 <= len(sys.argv[1:]):
price_checks = int(sys.argv[i + 2])
print("=" * 50)
print(" NS High-Value Card Tracker (API-Only)")
print(" No dump files needed. No automated trades.")
print(f" Scan range: 1–{max_id} | Price checks: {price_checks}")
print("=" * 50)
start = time.time()
init_db(DB_PATH)
# Step 1: Low ID scan
print(f"\n[SCAN] Scanning cards 1–{max_id} for Legendary/Epic...")
found = scan_low_id_cards(DB_PATH, max_id, callback=scan_callback)
print(f"[SCAN] Done. Found {len(found)} Legendary/Epic cards.")
# Step 2: CTE check
print(f"\n[CTE] Checking cards for dead nations...")
cte = scan_cte_cards(DB_PATH, callback=cte_callback)
print(f"[CTE] Found {len(cte)} dead nation cards.")
# Step 3: Historical check
print(f"\n[HIST] Checking against historical names...")
hist = scan_historical_cards(DB_PATH)
print(f"[HIST] Found {len(hist)} historical name matches.")
# Step 4: Price check
print(f"\n[PRICE] Checking live prices for up to {price_checks} targets...")
alerts, errors = check_target_prices(DB_PATH, price_checks)
if errors:
print(f" ⚠️ {errors} API errors during price check")
if alerts:
print(f"\n🚨 {len(alerts)} UNDERVALUED CARDS:")
for a in alerts:
print(f" {a['name']} (ID:{a['card_id']}) {a['rarity']} — Ask:{a['ask']:.2f} < {a['threshold']}")
print(f" {a['url']}")
else:
print("\n✅ No undervalued cards below threshold.")
# Step 5: Dashboard
generate_dashboard(alerts)
elapsed = time.time() - start
print(f"\n{'=' * 50}")
print(f" Done in {elapsed:.0f}s. Open dashboard.html to view.")
print(f"{'=' * 50}")
if __name__ == "__main__":
main()