┌─ nix/apps/challenge-mode-leaderboard.nix ──────────────────────────────────┐
│ diff --git a/nix/apps/challenge-mode-leaderboard.nix b/nix/apps/challenge-mode-lea │
│ derboard.nix │
│ new file mode 100644 │
│ index 0000000..659719c │
│ --- /dev/null │
│ +++ b/nix/apps/challenge-mode-leaderboard.nix │
│ @@ -0,0 +1,155 @@ │
│ +{ │
│ + writers, │
│ + api, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + fetcherScript = │
│ + writers.writePython3Bin "cm-leaderboard-fetcher" { │
│ + libraries = [python3Packages.requests]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import requests │
│ + import json │
│ + import re │
│ + import sys │
│ + import time │
│ + │
│ + ALL_REALMS = ${builtins.toJSON api.realm} │
│ + │
│ + # TODO: find repo root │
│ + OUTPUT_ROOT = "./web/public/data" │
│ + # TODO: fix me │
│ + API_TOKEN = os.getenv("BLIZZARD_API_TOKEN") │
│ + │
│ + def slugify(text): │
│ + # converts a string to a url friendly slug │
│ + text = text.lower() │
│ + text = re.sub(r'[\s\'\W]+', '-', text) │
│ + return text.strip('-') │
│ + │
│ + def get_current_period_and_dungeons(realm_info, session): │
│ + # fetch the current period ID and dungeon list │
│ + region, realm_id, name = realm_info['region'], realm_info['id'], realm_ │
│ info['name'] │
│ + namespace = f"dynamic-classic-{region}" │
│ + url = ( │
│ + f"https://{region}.api.blizzard.com/data/wow/connected-realm/" │
│ + f"{realm_id}/mythic-leaderboard/index?namespace={namespace}" │
│ + ) │
│ + │
│ + try: │
│ + print(f" Fetching leaderboard index for {name}...") │
│ + response = session.get(url, timeout=15) │
│ + response.raise_for_status() │
│ + data = response.json() │
│ + │
│ + href = data["current_leaderboards"][0]["key"]["href"] │
│ + match = re.search(r"/period/(\d+)", href) │
│ + if not match: │
│ + print(f" ERROR: Could not parse period ID for {name}.", file=s │
│ ys.stderr) │
│ + return None, None │
│ + period_id = match.group(1) │
│ + │
│ + dungeons = [] │
│ + for d in data["current_leaderboards"]: │
│ + dungeon_name_field = d["name"] │
│ + # check if the name field is a dictionary of localizations │
│ + if isinstance(dungeon_name_field, dict): │
│ + # if it is, pick the English name for consistency. │
│ + # use .get() for safety in case en_US is missing. │
│ + name_str = dungeon_name_field.get("en_US", "unknown-dungeon │
│ -name") │
│ + else: │
│ + # otherwise, it's already a string. │
│ + name_str = dungeon_name_field │
│ + │
│ + dungeons.append({ │
│ + "id": d["id"], │
│ + "name": name_str, # use extracted string name │
│ + "slug": slugify(name_str) │
│ + }) │
│ + │
│ + print(f" Found Period: {period_id}, Dungeons: {len(dungeons)}") │
│ + return period_id, dungeons │
│ + │
│ + except requests.exceptions.RequestException as e: │
│ + print(f" ERROR: API request failed for {name}: {e}", file=sys.stde │
│ rr) │
│ + return None, None │
│ + except (KeyError, IndexError) as e: │
│ + print(f" ERROR: Could not parse index for {name}. Details: {e}", f │
│ ile=sys.stderr) │
│ + return None, None │
│ + │
│ + │
│ + def get_leaderboard_data(realm_info, dungeon, period_id, session): │
│ + # fetch a specific leaderboard. │
│ + region, realm_id = realm_info['region'], realm_info['id'] │
│ + namespace = f"dynamic-classic-{region}" │
│ + url = ( │
│ + f"https://{region}.api.blizzard.com/data/wow/connected-realm/" │
│ + f"{realm_id}/mythic-leaderboard/{dungeon['id']}/period/" │
│ + f"{period_id}?namespace={namespace}" │
│ + ) │
│ + │
│ + try: │
│ + time.sleep(0.05) │
│ + response = session.get(url, timeout=15) │
│ + response.raise_for_status() │
│ + return response.json() │
│ + except requests.exceptions.RequestException as e: │
│ + print(f" ERROR fetching for {dungeon['name']}: {e}", file=sys.st │
│ derr) │
│ + return None │
│ + │
│ + │
│ + def main(): │
│ + if not API_TOKEN: │
│ + print( │
│ + "FATAL: BLIZZARD_API_TOKEN environment variable not set.", │
│ + file=sys.stderr │
│ + ) │
│ + sys.exit(1) │
│ + │
│ + print("Starting leaderboard fetch...") │
│ + print(f"Outputting data to: {os.path.abspath(OUTPUT_ROOT)}") │
│ + │
│ + session = requests.Session() │
│ + session.headers.update({"Authorization": f"Bearer {API_TOKEN}"}) │
│ + │
│ + for realm_slug, realm_info in ALL_REALMS.items(): │
│ + print( │
│ + f"\nProcessing Realm: {realm_info['name']} " │
│ + f"({realm_info['region'].upper()})" │
│ + ) │
│ + │
│ + period_id, dungeons = get_current_period_and_dungeons( │
│ + realm_info, session │
│ + ) │
│ + if not period_id or not dungeons: │
│ + print(f" Skipping realm {realm_info['name']}.") │
│ + continue │
│ + │
│ + for dungeon in dungeons: │
│ + print(f" - Fetching dungeon: {dungeon['name']}") │
│ + leaderboard = get_leaderboard_data( │
│ + realm_info, dungeon, period_id, session │
│ + ) │
│ + if not leaderboard: │
│ + continue │
│ + │
│ + output_path = os.path.join( │
│ + OUTPUT_ROOT, "challenge-mode", realm_info['region'], │
│ + realm_slug, dungeon['slug'], │
│ + f"{realm_slug}-{dungeon['slug']}-leaderboard.json" │
│ + ) │
│ + os.makedirs(os.path.dirname(output_path), exist_ok=True) │
│ + with open(output_path, 'w', encoding='utf-8') as f: │
│ + json.dump(leaderboard, f, indent=2) │
│ + │
│ + print("\nDone.") │
│ + │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + fetcherScript │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...hallenge-mode-leaderboard.nix ───┐
│ diff --git a/nix/apps/challenge-mode-leaderb │
│ oard.nix b/nix/apps/challenge-mode-leaderboa │
│ rd.nix │
│ new file mode 100644 │
│ index 0000000..659719c │
│ --- /dev/null │
│ +++ b/nix/apps/challenge-mode-leaderboard.ni │
│ x │
│ @@ -0,0 +1,155 @@ │
│ +{ │
│ + writers, │
│ + api, │
│ + python3Packages, │
│ + ... │
│ +}: let │
│ + fetcherScript = │
│ + writers.writePython3Bin "cm-leaderboard │
│ -fetcher" { │
│ + libraries = [python3Packages.requests │
│ ]; │
│ + doCheck = false; │
│ + } │
│ + '' │
│ + import os │
│ + import requests │
│ + import json │
│ + import re │
│ + import sys │
│ + import time │
│ + │
│ + ALL_REALMS = ${builtins.toJSON api.re │
│ alm} │
│ + │
│ + # TODO: find repo root │
│ + OUTPUT_ROOT = "./web/public/data" │
│ + # TODO: fix me │
│ + API_TOKEN = os.getenv("BLIZZARD_API_T │
│ OKEN") │
│ + │
│ + def slugify(text): │
│ + # converts a string to a url frie │
│ ndly slug │
│ + text = text.lower() │
│ + text = re.sub(r'[\s\'\W]+', '-', │
│ text) │
│ + return text.strip('-') │
│ + │
│ + def get_current_period_and_dungeons(r │
│ ealm_info, session): │
│ + # fetch the current period ID and │
│ dungeon list │
│ + region, realm_id, name = realm_in │
│ fo['region'], realm_info['id'], realm_info[' │
│ name'] │
│ + namespace = f"dynamic-classic-{re │
│ gion}" │
│ + url = ( │
│ + f"https://{region}.api.blizza │
│ rd.com/data/wow/connected-realm/" │
│ + f"{realm_id}/mythic-leaderboa │
│ rd/index?namespace={namespace}" │
│ + ) │
│ + │
│ + try: │
│ + print(f" Fetching leaderboar │
│ d index for {name}...") │
│ + response = session.get(url, t │
│ imeout=15) │
│ + response.raise_for_status() │
│ + data = response.json() │
│ + │
│ + href = data["current_leaderbo │
│ ards"][0]["key"]["href"] │
│ + match = re.search(r"/period/( │
│ \d+)", href) │
│ + if not match: │
│ + print(f" ERROR: Could no │
│ t parse period ID for {name}.", file=sys.std │
│ err) │
│ + return None, None │
│ + period_id = match.group(1) │
│ + │
│ + dungeons = [] │
│ + for d in data["current_leader │
│ boards"]: │
│ + dungeon_name_field = d["n │
│ ame"] │
│ + # check if the name field │
│ is a dictionary of localizations │
│ + if isinstance(dungeon_nam │
│ e_field, dict): │
│ + # if it is, pick the │
│ English name for consistency. │
│ + # use .get() for safe │
│ ty in case en_US is missing. │
│ + name_str = dungeon_na │
│ me_field.get("en_US", "unknown-dungeon-name" │
│ ) │
│ + else: │
│ + # otherwise, it's alr │
│ eady a string. │
│ + name_str = dungeon_na │
│ me_field │
│ + │
│ + dungeons.append({ │
│ + "id": d["id"], │
│ + "name": name_str, # u │
│ se extracted string name │
│ + "slug": slugify(name_ │
│ str) │
│ + }) │
│ + │
│ + print(f" Found Period: {peri │
│ od_id}, Dungeons: {len(dungeons)}") │
│ + return period_id, dungeons │
│ + │
│ + except requests.exceptions.Reques │
│ tException as e: │
│ + print(f" ERROR: API request │
│ failed for {name}: {e}", file=sys.stderr) │
│ + return None, None │
│ + except (KeyError, IndexError) as │
│ e: │
│ + print(f" ERROR: Could not pa │
│ rse index for {name}. Details: {e}", file=sy │
│ s.stderr) │
│ + return None, None │
│ + │
│ + │
│ + def get_leaderboard_data(realm_info, │
│ dungeon, period_id, session): │
│ + # fetch a specific leaderboard. │
│ + region, realm_id = realm_info['re │
│ gion'], realm_info['id'] │
│ + namespace = f"dynamic-classic-{re │
│ gion}" │
│ + url = ( │
│ + f"https://{region}.api.blizza │
│ rd.com/data/wow/connected-realm/" │
│ + f"{realm_id}/mythic-leaderboa │
│ rd/{dungeon['id']}/period/" │
│ + f"{period_id}?namespace={name │
│ space}" │
│ + ) │
│ + │
│ + try: │
│ + time.sleep(0.05) │
│ + response = session.get(url, t │
│ imeout=15) │
│ + response.raise_for_status() │
│ + return response.json() │
│ + except requests.exceptions.Reques │
│ tException as e: │
│ + print(f" ERROR fetching fo │
│ r {dungeon['name']}: {e}", file=sys.stderr) │
│ + return None │
│ + │
│ + │
│ + def main(): │
│ + if not API_TOKEN: │
│ + print( │
│ + "FATAL: BLIZZARD_API_TOKE │
│ N environment variable not set.", │
│ + file=sys.stderr │
│ + ) │
│ + sys.exit(1) │
│ + │
│ + print("Starting leaderboard fetch │
│ ...") │
│ + print(f"Outputting data to: {os.p │
│ ath.abspath(OUTPUT_ROOT)}") │
│ + │
│ + session = requests.Session() │
│ + session.headers.update({"Authoriz │
│ ation": f"Bearer {API_TOKEN}"}) │
│ + │
│ + for realm_slug, realm_info in ALL │
│ _REALMS.items(): │
│ + print( │
│ + f"\nProcessing Realm: {re │
│ alm_info['name']} " │
│ + f"({realm_info['region']. │
│ upper()})" │
│ + ) │
│ + │
│ + period_id, dungeons = get_cur │
│ rent_period_and_dungeons( │
│ + realm_info, session │
│ + ) │
│ + if not period_id or not dunge │
│ ons: │
│ + print(f" Skipping realm │
│ {realm_info['name']}.") │
│ + continue │
│ + │
│ + for dungeon in dungeons: │
│ + print(f" - Fetching dung │
│ eon: {dungeon['name']}") │
│ + leaderboard = get_leaderb │
│ oard_data( │
│ + realm_info, dungeon, │
│ period_id, session │
│ + ) │
│ + if not leaderboard: │
│ + continue │
│ + │
│ + output_path = os.path.joi │
│ n( │
│ + OUTPUT_ROOT, "challen │
│ ge-mode", realm_info['region'], │
│ + realm_slug, dungeon[' │
│ slug'], │
│ + f"{realm_slug}-{dunge │
│ on['slug']}-leaderboard.json" │
│ + ) │
│ + os.makedirs(os.path.dirna │
│ me(output_path), exist_ok=True) │
│ + with open(output_path, 'w │
│ ', encoding='utf-8') as f: │
│ + json.dump(leaderboard │
│ , f, indent=2) │
│ + │
│ + print("\nDone.") │
│ + │
│ + │
│ + if __name__ == "__main__": │
│ + main() │
│ + ''; │
│ +in │
│ + fetcherScript │
└──────────────────────────────────────────────┘
┌─ nix/apps/default.nix ─────────────────────────────────────────────────────┐
│ diff --git a/nix/apps/default.nix b/nix/apps/default.nix │
│ index 228534f..c908c6c 100644 │
│ --- a/nix/apps/default.nix │
│ +++ b/nix/apps/default.nix │
│ @@ -6,15 +6,17 @@ │
│ debuffs, │
│ inputs, │
│ trinket, │
│ + api, │
│ ... │
│ }: { │
│ perSystem = {pkgs, ...}: let │
│ - inherit (pkgs) callPackage writeShellApplication; │
│ + inherit (pkgs) callPackage writeShellApplication writers python3Packages; │
│ simulation = import ./simulation {inherit lib classes encounter buffs debuffs │
│ inputs trinket pkgs;}; │
│ getDB = callPackage ./getDB.nix {inherit inputs;}; │
│ testItemLookup = callPackage ./testItemLookup.nix {inherit inputs lib;}; │
│ testEnrichmentOutput = callPackage ./testEnrichmentOutput.nix {inherit inputs │
│ lib;}; │
│ testEquipmentEnrichment = callPackage ./testEquipmentEnrichment.nix {inherit │
│ inputs lib;}; │
│ + getCMLeaders = import ./challenge-mode-leaderboard.nix {inherit api writers p │
│ ython3Packages;}; │
│ │
│ # Trinket testing apps │
│ trinketTest = callPackage ./trinket-test.nix {inherit lib classes encounter b │
│ uffs debuffs inputs trinket writeShellApplication;}; │
│ @@ -59,6 +61,10 @@ │
│ type = "app"; │
│ program = "${trinketComparisonTest}/bin/trinket-comparison-test"; │
│ }; │
│ + getCM = { │
│ + type = "app"; │
│ + program = "${getCMLeaders}/bin/cm-leaderboard-fetcher"; │
│ + }; │
│ }; │
│ }; │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ nix/apps/default.nix ───────────────┐
│ diff --git a/nix/apps/default.nix b/nix/apps │
│ /default.nix │
│ index 228534f..c908c6c 100644 │
│ --- a/nix/apps/default.nix │
│ +++ b/nix/apps/default.nix │
│ @@ -6,15 +6,17 @@ │
│ debuffs, │
│ inputs, │
│ trinket, │
│ + api, │
│ ... │
│ }: { │
│ perSystem = {pkgs, ...}: let │
│ - inherit (pkgs) callPackage writeShellAp │
│ plication; │
│ + inherit (pkgs) callPackage writeShellAp │
│ plication writers python3Packages; │
│ simulation = import ./simulation {inher │
│ it lib classes encounter buffs debuffs input │
│ s trinket pkgs;}; │
│ getDB = callPackage ./getDB.nix {inheri │
│ t inputs;}; │
│ testItemLookup = callPackage ./testItem │
│ Lookup.nix {inherit inputs lib;}; │
│ testEnrichmentOutput = callPackage ./te │
│ stEnrichmentOutput.nix {inherit inputs lib;} │
│ ; │
│ testEquipmentEnrichment = callPackage . │
│ /testEquipmentEnrichment.nix {inherit inputs │
│ lib;}; │
│ + getCMLeaders = import ./challenge-mode- │
│ leaderboard.nix {inherit api writers python3 │
│ Packages;}; │
│ │
│ # Trinket testing apps │
│ trinketTest = callPackage ./trinket-tes │
│ t.nix {inherit lib classes encounter buffs d │
│ ebuffs inputs trinket writeShellApplication; │
│ }; │
│ @@ -59,6 +61,10 @@ │
│ type = "app"; │
│ program = "${trinketComparisonTes │
│ t}/bin/trinket-comparison-test"; │
│ }; │
│ + getCM = { │
│ + type = "app"; │
│ + program = "${getCMLeaders}/bin/cm │
│ -leaderboard-fetcher"; │
│ + }; │
│ }; │
│ }; │
│ } │
└──────────────────────────────────────────────┘