OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      167fbb4dc56c
DATE      2025-12-06
SUBJECT   discord: improve autocomplete, class color embed border
FILES     6 CHANGED
HASH      167fbb4dc56c
DATE      2025-12-06
SUBJECT   discord: improve autocomplete, class
          color embed border
FILES     6 CHANGED
 

diff --git a/web/api/discord.ts b/web/api/discord.ts
index db1b519..db6037b 100644
--- a/web/api/discord.ts
+++ b/web/api/discord.ts
@@ -15,6 +15,7 @@ import { handlePlayerCommand } from "../src/discord/commands/pla
yer.js";
 import { handleLeaderboardCommand } from "../src/discord/commands/leaderboard.js"
;
 import { autocompleteDungeon } from "../src/discord/autocomplete/dungeons.js";
 import { autocompleteRealm } from "../src/discord/autocomplete/realms.js";
+import { autocompleteRegionByPlayer } from "../src/discord/autocomplete/regions.j
s";
 import { autocompletePlayer } from "../src/discord/autocomplete/players.js";
 import { autocompleteClass } from "../src/discord/autocomplete/classes.js";
 import { createGenericErrorEmbed } from "../src/discord/embeds/error.js";
@@ -22,6 +23,10 @@ import { createGenericErrorEmbed } from "../src/discord/embeds/
error.js";
 // cache initialization flag
 let cacheInitialized = false;
 
+// refresh cooldown tracking (message_id -> timestamp)
+const refreshCooldowns = new Map<string, number>();
+const REFRESH_COOLDOWN_MS = 30000; // 30 seconds
+
 /**
  * Main Discord interaction handler
  */
@@ -147,7 +152,10 @@ async function handleAutocomplete(interaction: DiscordInterac
tion) {
     if (focusedOption.name === "realm") {
       const region = getOptionValue(options[0]?.options || [], "region");
       const regionStr = typeof region === "string" ? region : undefined;
-      return autocompleteRealm(query, regionStr as "us" | "eu" | "kr" | "tw" | un
defined);
+      return autocompleteRealm(
+        query,
+        regionStr as "us" | "eu" | "kr" | "tw" | undefined,
+      );
     }
     if (focusedOption.name === "class") {
       return autocompleteClass(query);
@@ -164,10 +172,34 @@ async function handleAutocomplete(interaction: DiscordIntera
ction) {
         typeof realm === "string" ? realm : undefined,
       );
     }
+    if (focusedOption.name === "region") {
+      const name = getOptionValue(options, "name");
+      console.log("[Discord Handler] All options:", JSON.stringify(options));
+      console.log(
+        "[Discord Handler] Region autocomplete - name option value:",
+        name,
+        "type:",
+        typeof name,
+      );
+      console.log(
+        "[Discord Handler] Focused option:",
+        JSON.stringify(focusedOption),
+      );
+      return autocompleteRegionByPlayer(
+        query,
+        typeof name === "string" ? name : undefined,
+      );
+    }
     if (focusedOption.name === "realm") {
       const region = getOptionValue(options, "region");
+      const name = getOptionValue(options, "name");
       const regionStr = typeof region === "string" ? region : undefined;
-      return autocompleteRealm(query, regionStr as "us" | "eu" | "kr" | "tw" | un
defined);
+      const nameStr = typeof name === "string" ? name : undefined;
+      return autocompleteRealm(
+        query,
+        regionStr as "us" | "eu" | "kr" | "tw" | undefined,
+        nameStr,
+      );
     }
   }
 
@@ -199,11 +231,38 @@ async function handleCommand(interaction: DiscordInteraction
) {
 async function handleButton(interaction: DiscordInteraction) {
   const customId = interaction.data?.custom_id || "";
 
+  // check cooldown for all refresh actions
+  if (customId.startsWith("refresh_")) {
+    const messageId = interaction.message?.id;
+    if (messageId) {
+      const lastRefresh = refreshCooldowns.get(messageId);
+      const now = Date.now();
+
+      if (lastRefresh && now - lastRefresh < REFRESH_COOLDOWN_MS) {
+        // silently ignore refresh attempts within cooldown
+        // return current message unchanged
+        return interaction.message || {};
+      }
+
+      // update cooldown timestamp
+      refreshCooldowns.set(messageId, now);
+
+      // cleanup old entries (older than 5 minutes)
+      for (const [id, timestamp] of refreshCooldowns.entries()) {
+        if (now - timestamp > 300000) {
+          refreshCooldowns.delete(id);
+        }
+      }
+    }
+  }
+
   // handle refresh player profile
   if (customId.startsWith("refresh_player:")) {
     const [, region, realm, name] = customId.split(":");
 
-    const { handlePlayerCommand } = await import("../src/discord/commands/player.
js");
+    const { handlePlayerCommand } = await import(
+      "../src/discord/commands/player.js"
+    );
 
     const fakeInteraction: DiscordInteraction = {
       ...interaction,
@@ -223,9 +282,12 @@ async function handleButton(interaction: DiscordInteraction) 
{
 
   // handle refresh dungeon leaderboard
   if (customId.startsWith("refresh_dungeon:")) {
-    const [, dungeon, scope, region, realm, limit, season] = customId.split(":");
+    const [, dungeon, scope, region, realm, limit, season] =
+      customId.split(":");
 
-    const { handleLeaderboardCommand } = await import("../src/discord/commands/le
aderboard.js");
+    const { handleLeaderboardCommand } = await import(
+      "../src/discord/commands/leaderboard.js"
+    );
 
     const options: any[] = [
       { name: "dungeon", type: 3, value: dungeon }, // STRING
@@ -250,9 +312,12 @@ async function handleButton(interaction: DiscordInteraction) 
{
 
   // handle refresh player leaderboard
   if (customId.startsWith("refresh_players:")) {
-    const [, scope, region, realm, className, limit, season] = customId.split(":"
);
+    const [, scope, region, realm, className, limit, season] =
+      customId.split(":");
 
-    const { handleLeaderboardCommand } = await import("../src/discord/commands/le
aderboard.js");
+    const { handleLeaderboardCommand } = await import(
+      "../src/discord/commands/leaderboard.js"
+    );
 
     const options: any[] = [{ name: "scope", type: 3, value: scope }]; // STRING
     if (region) options.push({ name: "region", type: 3, value: region }); // STRI
NG

diff --git a/web/api/discord.ts b/web/api/di
scord.ts
index db1b519..db6037b 100644
--- a/web/api/discord.ts
+++ b/web/api/discord.ts
@@ -15,6 +15,7 @@ import { handlePlayerComma
nd } from "../src/discord/commands/player.js
";
 import { handleLeaderboardCommand } from ".
./src/discord/commands/leaderboard.js";
 import { autocompleteDungeon } from "../src
/discord/autocomplete/dungeons.js";
 import { autocompleteRealm } from "../src/d
iscord/autocomplete/realms.js";
+import { autocompleteRegionByPlayer } from 
"../src/discord/autocomplete/regions.js";
 import { autocompletePlayer } from "../src/
discord/autocomplete/players.js";
 import { autocompleteClass } from "../src/d
iscord/autocomplete/classes.js";
 import { createGenericErrorEmbed } from "..
/src/discord/embeds/error.js";
@@ -22,6 +23,10 @@ import { createGenericErr
orEmbed } from "../src/discord/embeds/error.
js";
 // cache initialization flag
 let cacheInitialized = false;
 
+// refresh cooldown tracking (message_id ->
 timestamp)
+const refreshCooldowns = new Map<string, nu
mber>();
+const REFRESH_COOLDOWN_MS = 30000; // 30 se
conds
+
 /**
  * Main Discord interaction handler
  */
@@ -147,7 +152,10 @@ async function handleAu
tocomplete(interaction: DiscordInteraction) 
{
     if (focusedOption.name === "realm") {
       const region = getOptionValue(options
[0]?.options || [], "region");
       const regionStr = typeof region === "
string" ? region : undefined;
-      return autocompleteRealm(query, regio
nStr as "us" | "eu" | "kr" | "tw" | undefine
d);
+      return autocompleteRealm(
+        query,
+        regionStr as "us" | "eu" | "kr" | "
tw" | undefined,
+      );
     }
     if (focusedOption.name === "class") {
       return autocompleteClass(query);
@@ -164,10 +172,34 @@ async function handleA
utocomplete(interaction: DiscordInteraction)
 {
         typeof realm === "string" ? realm :
 undefined,
       );
     }
+    if (focusedOption.name === "region") {
+      const name = getOptionValue(options, 
"name");
+      console.log("[Discord Handler] All op
tions:", JSON.stringify(options));
+      console.log(
+        "[Discord Handler] Region autocompl
ete - name option value:",
+        name,
+        "type:",
+        typeof name,
+      );
+      console.log(
+        "[Discord Handler] Focused option:"
,
+        JSON.stringify(focusedOption),
+      );
+      return autocompleteRegionByPlayer(
+        query,
+        typeof name === "string" ? name : u
ndefined,
+      );
+    }
     if (focusedOption.name === "realm") {
       const region = getOptionValue(options
, "region");
+      const name = getOptionValue(options, 
"name");
       const regionStr = typeof region === "
string" ? region : undefined;
-      return autocompleteRealm(query, regio
nStr as "us" | "eu" | "kr" | "tw" | undefine
d);
+      const nameStr = typeof name === "stri
ng" ? name : undefined;
+      return autocompleteRealm(
+        query,
+        regionStr as "us" | "eu" | "kr" | "
tw" | undefined,
+        nameStr,
+      );
     }
   }
 
@@ -199,11 +231,38 @@ async function handleC
ommand(interaction: DiscordInteraction) {
 async function handleButton(interaction: Di
scordInteraction) {
   const customId = interaction.data?.custom
_id || "";
 
+  // check cooldown for all refresh actions
+  if (customId.startsWith("refresh_")) {
+    const messageId = interaction.message?.
id;
+    if (messageId) {
+      const lastRefresh = refreshCooldowns.
get(messageId);
+      const now = Date.now();
+
+      if (lastRefresh && now - lastRefresh 
< REFRESH_COOLDOWN_MS) {
+        // silently ignore refresh attempts
 within cooldown
+        // return current message unchanged
+        return interaction.message || {};
+      }
+
+      // update cooldown timestamp
+      refreshCooldowns.set(messageId, now);
+
+      // cleanup old entries (older than 5 
minutes)
+      for (const [id, timestamp] of refresh
Cooldowns.entries()) {
+        if (now - timestamp > 300000) {
+          refreshCooldowns.delete(id);
+        }
+      }
+    }
+  }
+
   // handle refresh player profile
   if (customId.startsWith("refresh_player:"
)) {
     const [, region, realm, name] = customI
d.split(":");
 
-    const { handlePlayerCommand } = await i
mport("../src/discord/commands/player.js");
+    const { handlePlayerCommand } = await i
mport(
+      "../src/discord/commands/player.js"
+    );
 
     const fakeInteraction: DiscordInteracti
on = {
       ...interaction,
@@ -223,9 +282,12 @@ async function handleBu
tton(interaction: DiscordInteraction) {
 
   // handle refresh dungeon leaderboard
   if (customId.startsWith("refresh_dungeon:
")) {
-    const [, dungeon, scope, region, realm,
 limit, season] = customId.split(":");
+    const [, dungeon, scope, region, realm,
 limit, season] =
+      customId.split(":");
 
-    const { handleLeaderboardCommand } = aw
ait import("../src/discord/commands/leaderbo
ard.js");
+    const { handleLeaderboardCommand } = aw
ait import(
+      "../src/discord/commands/leaderboard.
js"
+    );
 
     const options: any[] = [
       { name: "dungeon", type: 3, value: du
ngeon }, // STRING
@@ -250,9 +312,12 @@ async function handleBu
tton(interaction: DiscordInteraction) {
 
   // handle refresh player leaderboard
   if (customId.startsWith("refresh_players:
")) {
-    const [, scope, region, realm, classNam
e, limit, season] = customId.split(":");
+    const [, scope, region, realm, classNam
e, limit, season] =
+      customId.split(":");
 
-    const { handleLeaderboardCommand } = aw
ait import("../src/discord/commands/leaderbo
ard.js");
+    const { handleLeaderboardCommand } = aw
ait import(
+      "../src/discord/commands/leaderboard.
js"
+    );
 
     const options: any[] = [{ name: "scope"
, type: 3, value: scope }]; // STRING
     if (region) options.push({ name: "regio
n", type: 3, value: region }); // STRING
 

diff --git a/web/src/discord/autocomplete/realms.ts b/web/src/discord/autocomplete
/realms.ts
index 9381824..69a3610 100644
--- a/web/src/discord/autocomplete/realms.ts
+++ b/web/src/discord/autocomplete/realms.ts
@@ -1,56 +1,87 @@
 // Realm autocomplete handler
 
-import { getRealms } from "../indexes/cache.js";
+import { getRealms, getPlayers } from "../indexes/cache.js";
 import type { AutocompleteChoice } from "../types.js";
 import { DISCORD_LIMITS } from "../constants.js";
 
 /**
  * Handles autocomplete for realm selection
  * Filters by region and fuzzy matches on realm name
+ * Optionally filters by player name if provided
  */
 export async function autocompleteRealm(
-	query: string,
-	region?: "us" | "eu" | "kr" | "tw",
+  query: string,
+  region?: "us" | "eu" | "kr" | "tw",
+  playerName?: string,
 ): Promise<AutocompleteChoice[]> {
-	// if no region specified, return empty (user must select region first)
-	if (!region) {
-		return [
-			{
-				name: "Please select a region first",
-				value: "none",
-			},
-		];
-	}
-
-	const realms = await getRealms(region);
-	const lowerQuery = query.toLowerCase().trim();
-
-	// sort by player count (most popular first)
-	const sortedRealms = [...realms].sort(
-		(a, b) => b.player_count - a.player_count,
-	);
-
-	// if no query, return top realms
-	if (!lowerQuery) {
-		return sortedRealms
-			.slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLETE_CHOICES)
-			.map((r) => ({
-				name: `${r.name} (${r.player_count.toLocaleString()} players)`,
-				value: r.slug,
-			}));
-	}
-
-	// filter realms by name or slug
-	const filtered = sortedRealms
-		.filter(
-			(r) =>
-				r.name.toLowerCase().includes(lowerQuery) ||
-				r.slug.toLowerCase().includes(lowerQuery),
-		)
-		.slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLETE_CHOICES);
-
-	return filtered.map((r) => ({
-		name: `${r.name} (${r.player_count.toLocaleString()} players)`,
-		value: r.slug,
-	}));
+  // if no region specified, return empty (user must select region first)
+  if (!region) {
+    return [
+      {
+        name: "Please select a region first",
+        value: "none",
+      },
+    ];
+  }
+
+  let realms = await getRealms(region);
+  const lowerQuery = query.toLowerCase().trim();
+
+  // if player name is provided, filter realms to only those where player exists
+  if (playerName && playerName.trim() !== "") {
+    const players = await getPlayers();
+    const lowerPlayerName = playerName.toLowerCase().trim();
+
+    // find all players matching the name in this region
+    const matchingPlayers = players.filter(
+      (p) =>
+        p.name.toLowerCase().includes(lowerPlayerName) &&
+        p.region === region.toLowerCase(),
+    );
+
+    // extract unique realm slugs from matching players
+    const playerRealms = new Set(matchingPlayers.map((p) => p.realm_slug));
+
+    // filter realms to only those where the player exists
+    realms = realms.filter((r) => playerRealms.has(r.slug));
+
+    // if no realms found, show helpful message
+    if (realms.length === 0) {
+      return [
+        {
+          name: `No players named "${playerName}" found in ${region.toUpperCase()
}`,
+          value: "none",
+        },
+      ];
+    }
+  }
+
+  // sort by player count (most popular first)
+  const sortedRealms = [...realms].sort(
+    (a, b) => b.player_count - a.player_count,
+  );
+
+  // if no query, return top realms
+  if (!lowerQuery) {
+    return sortedRealms
+      .slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLETE_CHOICES)
+      .map((r) => ({
+        name: r.name,
+        value: r.slug,
+      }));
+  }
+
+  // filter realms by name or slug
+  const filtered = sortedRealms
+    .filter(
+      (r) =>
+        r.name.toLowerCase().includes(lowerQuery) ||
+        r.slug.toLowerCase().includes(lowerQuery),
+    )
+    .slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLETE_CHOICES);
+
+  return filtered.map((r) => ({
+    name: r.name,
+    value: r.slug,
+  }));
 }

diff --git a/web/src/discord/autocomplete/re
alms.ts b/web/src/discord/autocomplete/realm
s.ts
index 9381824..69a3610 100644
--- a/web/src/discord/autocomplete/realms.ts
+++ b/web/src/discord/autocomplete/realms.ts
@@ -1,56 +1,87 @@
 // Realm autocomplete handler
 
-import { getRealms } from "../indexes/cache
.js";
+import { getRealms, getPlayers } from "../i
ndexes/cache.js";
 import type { AutocompleteChoice } from "..
/types.js";
 import { DISCORD_LIMITS } from "../constant
s.js";
 
 /**
  * Handles autocomplete for realm selection
  * Filters by region and fuzzy matches on r
ealm name
+ * Optionally filters by player name if pro
vided
  */
 export async function autocompleteRealm(
-	query: string,
-	region?: "us" | "eu" | "kr" | "tw",
+  query: string,
+  region?: "us" | "eu" | "kr" | "tw",
+  playerName?: string,
 ): Promise<AutocompleteChoice[]> {
-	// if no region specified, return empty (u
ser must select region first)
-	if (!region) {
-		return [
-			{
-				name: "Please select a region first",
-				value: "none",
-			},
-		];
-	}
-
-	const realms = await getRealms(region);
-	const lowerQuery = query.toLowerCase().tri
m();
-
-	// sort by player count (most popular firs
t)
-	const sortedRealms = [...realms].sort(
-		(a, b) => b.player_count - a.player_count
,
-	);
-
-	// if no query, return top realms
-	if (!lowerQuery) {
-		return sortedRealms
-			.slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLET
E_CHOICES)
-			.map((r) => ({
-				name: `${r.name} (${r.player_count.toLo
caleString()} players)`,
-				value: r.slug,
-			}));
-	}
-
-	// filter realms by name or slug
-	const filtered = sortedRealms
-		.filter(
-			(r) =>
-				r.name.toLowerCase().includes(lowerQuer
y) ||
-				r.slug.toLowerCase().includes(lowerQuer
y),
-		)
-		.slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLETE
_CHOICES);
-
-	return filtered.map((r) => ({
-		name: `${r.name} (${r.player_count.toLoca
leString()} players)`,
-		value: r.slug,
-	}));
+  // if no region specified, return empty (
user must select region first)
+  if (!region) {
+    return [
+      {
+        name: "Please select a region first
",
+        value: "none",
+      },
+    ];
+  }
+
+  let realms = await getRealms(region);
+  const lowerQuery = query.toLowerCase().tr
im();
+
+  // if player name is provided, filter rea
lms to only those where player exists
+  if (playerName && playerName.trim() !== "
") {
+    const players = await getPlayers();
+    const lowerPlayerName = playerName.toLo
werCase().trim();
+
+    // find all players matching the name i
n this region
+    const matchingPlayers = players.filter(
+      (p) =>
+        p.name.toLowerCase().includes(lower
PlayerName) &&
+        p.region === region.toLowerCase(),
+    );
+
+    // extract unique realm slugs from matc
hing players
+    const playerRealms = new Set(matchingPl
ayers.map((p) => p.realm_slug));
+
+    // filter realms to only those where th
e player exists
+    realms = realms.filter((r) => playerRea
lms.has(r.slug));
+
+    // if no realms found, show helpful mes
sage
+    if (realms.length === 0) {
+      return [
+        {
+          name: `No players named "${player
Name}" found in ${region.toUpperCase()}`,
+          value: "none",
+        },
+      ];
+    }
+  }
+
+  // sort by player count (most popular fir
st)
+  const sortedRealms = [...realms].sort(
+    (a, b) => b.player_count - a.player_cou
nt,
+  );
+
+  // if no query, return top realms
+  if (!lowerQuery) {
+    return sortedRealms
+      .slice(0, DISCORD_LIMITS.MAX_AUTOCOMP
LETE_CHOICES)
+      .map((r) => ({
+        name: r.name,
+        value: r.slug,
+      }));
+  }
+
+  // filter realms by name or slug
+  const filtered = sortedRealms
+    .filter(
+      (r) =>
+        r.name.toLowerCase().includes(lower
Query) ||
+        r.slug.toLowerCase().includes(lower
Query),
+    )
+    .slice(0, DISCORD_LIMITS.MAX_AUTOCOMPLE
TE_CHOICES);
+
+  return filtered.map((r) => ({
+    name: r.name,
+    value: r.slug,
+  }));
 }
 

diff --git a/web/src/discord/autocomplete/regions.ts b/web/src/discord/autocomplet
e/regions.ts
new file mode 100644
index 0000000..fbb1053
--- /dev/null
+++ b/web/src/discord/autocomplete/regions.ts
@@ -0,0 +1,77 @@
+// region autocomplete handler
+
+import { getPlayers } from "../indexes/cache.js";
+import type { AutocompleteChoice } from "../types.js";
+import { DISCORD_LIMITS } from "../constants.js";
+
+/**
+ * handles autocomplete for region selection based on player name
+ * filters regions to only show those where the specified player exists
+ */
+export async function autocompleteRegionByPlayer(
+  query: string,
+  playerName?: string,
+): Promise<AutocompleteChoice[]> {
+  // Add debug logging
+  console.log("[Region Autocomplete] playerName:", playerName, "query:", query);
+
+  // if no player name provided, return all regions
+  if (!playerName || playerName.trim() === "") {
+    console.log("[Region Autocomplete] No player name, returning all regions");
+    return [
+      { name: "US", value: "us" },
+      { name: "EU", value: "eu" },
+      { name: "KR", value: "kr" },
+      { name: "TW", value: "tw" },
+    ];
+  }
+
+  const players = await getPlayers();
+  const lowerPlayerName = playerName.toLowerCase().trim();
+
+  // find all players matching the name (exact match first, then partial)
+  let matchingPlayers = players.filter(
+    (p) => p.name.toLowerCase() === lowerPlayerName,
+  );
+
+  // if no exact matches, try partial match
+  if (matchingPlayers.length === 0) {
+    matchingPlayers = players.filter((p) =>
+      p.name.toLowerCase().includes(lowerPlayerName),
+    );
+  }
+
+  // extract unique regions from matching players
+  const regions = new Set<string>();
+  for (const player of matchingPlayers) {
+    regions.add(player.region);
+  }
+
+  // convert to autocomplete choices
+  const regionMap: Record<string, string> = {
+    us: "US",
+    eu: "EU",
+    kr: "KR",
+    tw: "TW",
+  };
+
+  const choices: AutocompleteChoice[] = [];
+  for (const region of Array.from(regions).sort()) {
+    choices.push({
+      name: regionMap[region] || region.toUpperCase(),
+      value: region,
+    });
+  }
+
+  // if no regions found, show helpful message
+  if (choices.length === 0) {
+    return [
+      {
+        name: `No players found matching "${playerName}"`,
+        value: "none",
+      },
+    ];
+  }
+
+  return choices;
+}

diff --git a/web/src/discord/autocomplete/re
gions.ts b/web/src/discord/autocomplete/regi
ons.ts
new file mode 100644
index 0000000..fbb1053
--- /dev/null
+++ b/web/src/discord/autocomplete/regions.t
s
@@ -0,0 +1,77 @@
+// region autocomplete handler
+
+import { getPlayers } from "../indexes/cach
e.js";
+import type { AutocompleteChoice } from "..
/types.js";
+import { DISCORD_LIMITS } from "../constant
s.js";
+
+/**
+ * handles autocomplete for region selectio
n based on player name
+ * filters regions to only show those where
 the specified player exists
+ */
+export async function autocompleteRegionByP
layer(
+  query: string,
+  playerName?: string,
+): Promise<AutocompleteChoice[]> {
+  // Add debug logging
+  console.log("[Region Autocomplete] player
Name:", playerName, "query:", query);
+
+  // if no player name provided, return all
 regions
+  if (!playerName || playerName.trim() === 
"") {
+    console.log("[Region Autocomplete] No p
layer name, returning all regions");
+    return [
+      { name: "US", value: "us" },
+      { name: "EU", value: "eu" },
+      { name: "KR", value: "kr" },
+      { name: "TW", value: "tw" },
+    ];
+  }
+
+  const players = await getPlayers();
+  const lowerPlayerName = playerName.toLowe
rCase().trim();
+
+  // find all players matching the name (ex
act match first, then partial)
+  let matchingPlayers = players.filter(
+    (p) => p.name.toLowerCase() === lowerPl
ayerName,
+  );
+
+  // if no exact matches, try partial match
+  if (matchingPlayers.length === 0) {
+    matchingPlayers = players.filter((p) =>
+      p.name.toLowerCase().includes(lowerPl
ayerName),
+    );
+  }
+
+  // extract unique regions from matching p
layers
+  const regions = new Set<string>();
+  for (const player of matchingPlayers) {
+    regions.add(player.region);
+  }
+
+  // convert to autocomplete choices
+  const regionMap: Record<string, string> =
 {
+    us: "US",
+    eu: "EU",
+    kr: "KR",
+    tw: "TW",
+  };
+
+  const choices: AutocompleteChoice[] = [];
+  for (const region of Array.from(regions).
sort()) {
+    choices.push({
+      name: regionMap[region] || region.toU
pperCase(),
+      value: region,
+    });
+  }
+
+  // if no regions found, show helpful mess
age
+  if (choices.length === 0) {
+    return [
+      {
+        name: `No players found matching "$
{playerName}"`,
+        value: "none",
+      },
+    ];
+  }
+
+  return choices;
+}
 

diff --git a/web/src/discord/constants.ts b/web/src/discord/constants.ts
index 9cbc2af..60a9e7c 100644
--- a/web/src/discord/constants.ts
+++ b/web/src/discord/constants.ts
@@ -1,16 +1,16 @@
-// colors for embeds (hex converted to decimal)
-export const COLORS = {
-  ARTIFACT: 0xe6cc80, // gold/legendary
-  EXCELLENT: 0xa335ee, // purple/epic
-  EPIC: 0xa335ee, // purple/epic
-  RARE: 0x0070dd, // blue/rare
-  UNCOMMON: 0x1eff00, // green/uncommon
-  COMMON: 0x9d9d9d, // gray/common
-  DEFAULT: 0x5865f2, // discord blurple
-  SUCCESS: 0x57f287, // green
-  WARNING: 0xfee75c, // yellow
-  ERROR: 0xed4245, // red
-  INFO: 0x3498db, // blue
+// class colors for embeds (hex converted to decimal)
+export const CLASS_COLORS = {
+  death_knight: 0xc41e3a,
+  druid: 0xff7c0a,
+  hunter: 0xaad372,
+  mage: 0x3fc7eb,
+  monk: 0x00ff98,
+  paladin: 0xf48cba,
+  priest: 0xffffff,
+  rogue: 0xfff468,
+  shaman: 0x0070dd,
+  warlock: 0x8788ee,
+  warrior: 0xc69b6d,
 } as const;
 
 // discord custom emoji IDs for specs
@@ -77,16 +77,6 @@ export const DUNGEON_EMOJI_IDS: Record<string, string> = {
   "temple-of-the-jade-serpent": "1446337487612936332",
 };
 
-// ranking bracket colors (match website colors)
-export const BRACKET_COLORS = {
-  artifact: COLORS.ARTIFACT,
-  excellent: COLORS.EXCELLENT,
-  epic: COLORS.EPIC,
-  rare: COLORS.RARE,
-  uncommon: COLORS.UNCOMMON,
-  common: COLORS.COMMON,
-} as const;
-
 // ranking bracket display names
 export const BRACKET_NAMES = {
   artifact: "Artifact",

diff --git a/web/src/discord/constants.ts b/
web/src/discord/constants.ts
index 9cbc2af..60a9e7c 100644
--- a/web/src/discord/constants.ts
+++ b/web/src/discord/constants.ts
@@ -1,16 +1,16 @@
-// colors for embeds (hex converted to deci
mal)
-export const COLORS = {
-  ARTIFACT: 0xe6cc80, // gold/legendary
-  EXCELLENT: 0xa335ee, // purple/epic
-  EPIC: 0xa335ee, // purple/epic
-  RARE: 0x0070dd, // blue/rare
-  UNCOMMON: 0x1eff00, // green/uncommon
-  COMMON: 0x9d9d9d, // gray/common
-  DEFAULT: 0x5865f2, // discord blurple
-  SUCCESS: 0x57f287, // green
-  WARNING: 0xfee75c, // yellow
-  ERROR: 0xed4245, // red
-  INFO: 0x3498db, // blue
+// class colors for embeds (hex converted t
o decimal)
+export const CLASS_COLORS = {
+  death_knight: 0xc41e3a,
+  druid: 0xff7c0a,
+  hunter: 0xaad372,
+  mage: 0x3fc7eb,
+  monk: 0x00ff98,
+  paladin: 0xf48cba,
+  priest: 0xffffff,
+  rogue: 0xfff468,
+  shaman: 0x0070dd,
+  warlock: 0x8788ee,
+  warrior: 0xc69b6d,
 } as const;
 
 // discord custom emoji IDs for specs
@@ -77,16 +77,6 @@ export const DUNGEON_EMOJ
I_IDS: Record<string, string> = {
   "temple-of-the-jade-serpent": "1446337487
612936332",
 };
 
-// ranking bracket colors (match website co
lors)
-export const BRACKET_COLORS = {
-  artifact: COLORS.ARTIFACT,
-  excellent: COLORS.EXCELLENT,
-  epic: COLORS.EPIC,
-  rare: COLORS.RARE,
-  uncommon: COLORS.UNCOMMON,
-  common: COLORS.COMMON,
-} as const;
-
 // ranking bracket display names
 export const BRACKET_NAMES = {
   artifact: "Artifact",
 

diff --git a/web/src/discord/embeds/player-profile.ts b/web/src/discord/embeds/pla
yer-profile.ts
index a86be52..9cdd9b9 100644
--- a/web/src/discord/embeds/player-profile.ts
+++ b/web/src/discord/embeds/player-profile.ts
@@ -7,6 +7,7 @@ import {
   SPEC_EMOJI_IDS,
   API_BASE_URL,
   DUNGEON_EMOJI_IDS,
+  CLASS_COLORS,
 } from "../constants.js";
 import { formatDurationFromMs } from "../../lib/utils.js";
 
@@ -68,15 +69,18 @@ export function createPlayerProfileEmbed(profile: PlayerProfil
eData): Embed {
 
     if (seasonData.global_ranking) {
       // only show percentile if not rank 1 (would be redundant)
-      const percentileText = seasonData.global_ranking === 1 ? "" : ` (${globalPe
rcentile})`;
+      const percentileText =
+        seasonData.global_ranking === 1 ? "" : ` (${globalPercentile})`;
       rankingsText += `**Global:** #${seasonData.global_ranking}${percentileText}
\n`;
     }
     if (seasonData.regional_ranking) {
-      const percentileText = seasonData.regional_ranking === 1 ? "" : ` (${region
alPercentile})`;
+      const percentileText =
+        seasonData.regional_ranking === 1 ? "" : ` (${regionalPercentile})`;
       rankingsText += `**Regional:** #${seasonData.regional_ranking}${percentileT
ext}\n`;
     }
     if (seasonData.realm_ranking) {
-      const percentileText = seasonData.realm_ranking === 1 ? "" : ` (${realmPerc
entile})`;
+      const percentileText =
+        seasonData.realm_ranking === 1 ? "" : ` (${realmPercentile})`;
       rankingsText += `**Realm:** #${seasonData.realm_ranking}${percentileText}`;
     }
 
@@ -113,10 +117,17 @@ export function createPlayerProfileEmbed(profile: PlayerProf
ileData): Embed {
     }
   }
 
+  // get class color for embed sidebar
+  const classKey = player.class_name
+    ?.toLowerCase()
+    .replace(/\s+/g, "_") as keyof typeof CLASS_COLORS;
+  const color = CLASS_COLORS[classKey] || 0x5865f2; // fallback to discord blurpl
e
+
   return {
     title,
     description,
     fields,
+    color,
     footer: {
       text: "wowsimstats.com",
     },

diff --git a/web/src/discord/embeds/player-p
rofile.ts b/web/src/discord/embeds/player-pr
ofile.ts
index a86be52..9cdd9b9 100644
--- a/web/src/discord/embeds/player-profile.
ts
+++ b/web/src/discord/embeds/player-profile.
ts
@@ -7,6 +7,7 @@ import {
   SPEC_EMOJI_IDS,
   API_BASE_URL,
   DUNGEON_EMOJI_IDS,
+  CLASS_COLORS,
 } from "../constants.js";
 import { formatDurationFromMs } from "../..
/lib/utils.js";
 
@@ -68,15 +69,18 @@ export function createPl
ayerProfileEmbed(profile: PlayerProfileData)
: Embed {
 
     if (seasonData.global_ranking) {
       // only show percentile if not rank 1
 (would be redundant)
-      const percentileText = seasonData.glo
bal_ranking === 1 ? "" : ` (${globalPercenti
le})`;
+      const percentileText =
+        seasonData.global_ranking === 1 ? "
" : ` (${globalPercentile})`;
       rankingsText += `**Global:** #${seaso
nData.global_ranking}${percentileText}\n`;
     }
     if (seasonData.regional_ranking) {
-      const percentileText = seasonData.reg
ional_ranking === 1 ? "" : ` (${regionalPerc
entile})`;
+      const percentileText =
+        seasonData.regional_ranking === 1 ?
 "" : ` (${regionalPercentile})`;
       rankingsText += `**Regional:** #${sea
sonData.regional_ranking}${percentileText}\n
`;
     }
     if (seasonData.realm_ranking) {
-      const percentileText = seasonData.rea
lm_ranking === 1 ? "" : ` (${realmPercentile
})`;
+      const percentileText =
+        seasonData.realm_ranking === 1 ? ""
 : ` (${realmPercentile})`;
       rankingsText += `**Realm:** #${season
Data.realm_ranking}${percentileText}`;
     }
 
@@ -113,10 +117,17 @@ export function create
PlayerProfileEmbed(profile: PlayerProfileDat
a): Embed {
     }
   }
 
+  // get class color for embed sidebar
+  const classKey = player.class_name
+    ?.toLowerCase()
+    .replace(/\s+/g, "_") as keyof typeof C
LASS_COLORS;
+  const color = CLASS_COLORS[classKey] || 0
x5865f2; // fallback to discord blurple
+
   return {
     title,
     description,
     fields,
+    color,
     footer: {
       text: "wowsimstats.com",
     },
 

diff --git a/web/src/scripts/deploy-commands.ts b/web/src/scripts/deploy-commands.
ts
index acf1ff6..274fdfc 100644
--- a/web/src/scripts/deploy-commands.ts
+++ b/web/src/scripts/deploy-commands.ts
@@ -1,5 +1,5 @@
-// Discord command registration script
-// Run this to register slash commands with Discord
+// discord command registration script
+// run this to register slash commands with Discord
 
 import { REST } from "@discordjs/rest";
 import { Routes } from "discord-api-types/v10";
@@ -10,190 +10,191 @@ const DISCORD_APPLICATION_ID = process.env.DISCORD_APPLICATI
ON_ID;
 const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN;
 
 if (!DISCORD_APPLICATION_ID || !DISCORD_BOT_TOKEN) {
-	console.error("Error: DISCORD_APPLICATION_ID and DISCORD_BOT_TOKEN must be set")
;
-	console.error("Please set these environment variables and try again.");
-	process.exit(1);
+  console.error(
+    "Error: DISCORD_APPLICATION_ID and DISCORD_BOT_TOKEN must be set",
+  );
+  console.error("Please set these environment variables and try again.");
+  process.exit(1);
 }
 
 // define commands
 const commands = [
-	// /player command
-	new SlashCommandBuilder()
-		.setName("player")
-		.setDescription("Look up a WoW Challenge Mode player")
-		.addStringOption((option) =>
-			option
-				.setName("name")
-				.setDescription("Player name")
-				.setRequired(true)
-				.setAutocomplete(true),
-		)
-		.addStringOption((option) =>
-			option
-				.setName("region")
-				.setDescription("Region")
-				.setRequired(true)
-				.addChoices(
-					{ name: "US", value: "us" },
-					{ name: "EU", value: "eu" },
-					{ name: "KR", value: "kr" },
-					{ name: "TW", value: "tw" },
-				),
-		)
-		.addStringOption((option) =>
-			option
-				.setName("realm")
-				.setDescription("Realm")
-				.setRequired(true)
-				.setAutocomplete(true),
-		),
+  // /player command
+  new SlashCommandBuilder()
+    .setName("player")
+    .setDescription("Look up a WoW Challenge Mode player")
+    .addStringOption((option) =>
+      option
+        .setName("name")
+        .setDescription("Player name")
+        .setRequired(true)
+        .setAutocomplete(true),
+    )
+    .addStringOption((option) =>
+      option
+        .setName("region")
+        .setDescription("Region")
+        .setRequired(true)
+        .setAutocomplete(true),
+    )
+    .addStringOption((option) =>
+      option
+        .setName("realm")
+        .setDescription("Realm")
+        .setRequired(true)
+        .setAutocomplete(true),
+    ),
 
-	// /leaderboard command
-	new SlashCommandBuilder()
-		.setName("leaderboard")
-		.setDescription("View WoW Challenge Mode leaderboards")
-		.addSubcommand((subcommand) =>
-			subcommand
-				.setName("dungeon")
-				.setDescription("View dungeon leaderboard")
-				.addStringOption((option) =>
-					option
-						.setName("dungeon")
-						.setDescription("Choose a dungeon")
-						.setRequired(true)
-						.setAutocomplete(true),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("scope")
-						.setDescription("Leaderboard scope")
-						.setRequired(true)
-						.addChoices(
-							{ name: "Global", value: "global" },
-							{ name: "Region", value: "region" },
-							{ name: "Realm", value: "realm" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("region")
-						.setDescription("Region (required for regional/realm scope)")
-						.setRequired(false)
-						.addChoices(
-							{ name: "US", value: "us" },
-							{ name: "EU", value: "eu" },
-							{ name: "KR", value: "kr" },
-							{ name: "TW", value: "tw" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("realm")
-						.setDescription("Realm (required for realm scope)")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addIntegerOption((option) =>
-					option
-						.setName("limit")
-						.setDescription("Number of runs to display (default: 10, max: 15)")
-						.setRequired(false)
-						.setMinValue(1)
-						.setMaxValue(15),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("season")
-						.setDescription("Season (default: current season)")
-						.setRequired(false),
-				),
-		)
-		.addSubcommand((subcommand) =>
-			subcommand
-				.setName("players")
-				.setDescription("View player rankings")
-				.addStringOption((option) =>
-					option
-						.setName("scope")
-						.setDescription("Leaderboard scope")
-						.setRequired(true)
-						.addChoices(
-							{ name: "Global", value: "global" },
-							{ name: "Region", value: "region" },
-							{ name: "Realm", value: "realm" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("region")
-						.setDescription("Region (required for regional/realm scope)")
-						.setRequired(false)
-						.addChoices(
-							{ name: "US", value: "us" },
-							{ name: "EU", value: "eu" },
-							{ name: "KR", value: "kr" },
-							{ name: "TW", value: "tw" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("realm")
-						.setDescription("Realm (required for realm scope)")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("class")
-						.setDescription("Filter by class")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addIntegerOption((option) =>
-					option
-						.setName("limit")
-						.setDescription("Number of players to display (default: 25, max: 25)")
-						.setRequired(false)
-						.setMinValue(1)
-						.setMaxValue(25),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("season")
-						.setDescription("Season (default: current season)")
-						.setRequired(false),
-				),
-		),
+  // /leaderboard command
+  new SlashCommandBuilder()
+    .setName("leaderboard")
+    .setDescription("View WoW Challenge Mode leaderboards")
+    .addSubcommand((subcommand) =>
+      subcommand
+        .setName("dungeon")
+        .setDescription("View dungeon leaderboard")
+        .addStringOption((option) =>
+          option
+            .setName("dungeon")
+            .setDescription("Choose a dungeon")
+            .setRequired(true)
+            .setAutocomplete(true),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("scope")
+            .setDescription("Leaderboard scope")
+            .setRequired(true)
+            .addChoices(
+              { name: "Global", value: "global" },
+              { name: "Region", value: "region" },
+              { name: "Realm", value: "realm" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("region")
+            .setDescription("Region (required for regional/realm scope)")
+            .setRequired(false)
+            .addChoices(
+              { name: "US", value: "us" },
+              { name: "EU", value: "eu" },
+              { name: "KR", value: "kr" },
+              { name: "TW", value: "tw" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("realm")
+            .setDescription("Realm (required for realm scope)")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addIntegerOption((option) =>
+          option
+            .setName("limit")
+            .setDescription("Number of runs to display (default: 10, max: 15)")
+            .setRequired(false)
+            .setMinValue(1)
+            .setMaxValue(15),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("season")
+            .setDescription("Season (default: current season)")
+            .setRequired(false),
+        ),
+    )
+    .addSubcommand((subcommand) =>
+      subcommand
+        .setName("players")
+        .setDescription("View player rankings")
+        .addStringOption((option) =>
+          option
+            .setName("scope")
+            .setDescription("Leaderboard scope")
+            .setRequired(true)
+            .addChoices(
+              { name: "Global", value: "global" },
+              { name: "Region", value: "region" },
+              { name: "Realm", value: "realm" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("region")
+            .setDescription("Region (required for regional/realm scope)")
+            .setRequired(false)
+            .addChoices(
+              { name: "US", value: "us" },
+              { name: "EU", value: "eu" },
+              { name: "KR", value: "kr" },
+              { name: "TW", value: "tw" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("realm")
+            .setDescription("Realm (required for realm scope)")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("class")
+            .setDescription("Filter by class")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addIntegerOption((option) =>
+          option
+            .setName("limit")
+            .setDescription(
+              "Number of players to display (default: 25, max: 25)",
+            )
+            .setRequired(false)
+            .setMinValue(1)
+            .setMaxValue(25),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("season")
+            .setDescription("Season (default: current season)")
+            .setRequired(false),
+        ),
+    ),
 ].map((command) => command.toJSON());
 
 // register commands with Discord
 async function deployCommands() {
-	console.log("Registering Discord slash commands...");
-	console.log(`Application ID: ${DISCORD_APPLICATION_ID}`);
-	console.log(`Commands to register: ${commands.length}`);
+  console.log("Registering Discord slash commands...");
+  console.log(`Application ID: ${DISCORD_APPLICATION_ID}`);
+  console.log(`Commands to register: ${commands.length}`);
 
-	const rest = new REST({ version: "10" }).setToken(DISCORD_BOT_TOKEN);
+  const rest = new REST({ version: "10" }).setToken(DISCORD_BOT_TOKEN);
 
-	try {
-		console.log("Sending registration request to Discord...");
+  try {
+    console.log("Sending registration request to Discord...");
 
-		const data = await rest.put(
-			Routes.applicationCommands(DISCORD_APPLICATION_ID),
-			{ body: commands },
-		) as any[];
+    const data = (await rest.put(
+      Routes.applicationCommands(DISCORD_APPLICATION_ID),
+      { body: commands },
+    )) as any[];
 
-		console.log(`? Successfully registered ${data.length} slash commands!`);
-		console.log("\nRegistered commands:");
-		data.forEach((cmd: any) => {
-			console.log(`  - /${cmd.name}: ${cmd.description}`);
-		});
+    console.log(`? Successfully registered ${data.length} slash commands!`);
+    console.log("\nRegistered commands:");
+    data.forEach((cmd: any) => {
+      console.log(`  - /${cmd.name}: ${cmd.description}`);
+    });
 
-		console.log("\nYou can now use these commands in Discord!");
-		console.log("Note: It may take a few minutes for commands to appear in all serv
ers.");
-	} catch (error) {
-		console.error("? Error registering commands:", error);
-		process.exit(1);
-	}
+    console.log("\nYou can now use these commands in Discord!");
+    console.log(
+      "Note: It may take a few minutes for commands to appear in all servers.",
+    );
+  } catch (error) {
+    console.error("? Error registering commands:", error);
+    process.exit(1);
+  }
 }
 
 // run the deployment

diff --git a/web/src/scripts/deploy-commands
.ts b/web/src/scripts/deploy-commands.ts
index acf1ff6..274fdfc 100644
--- a/web/src/scripts/deploy-commands.ts
+++ b/web/src/scripts/deploy-commands.ts
@@ -1,5 +1,5 @@
-// Discord command registration script
-// Run this to register slash commands with
 Discord
+// discord command registration script
+// run this to register slash commands with
 Discord
 
 import { REST } from "@discordjs/rest";
 import { Routes } from "discord-api-types/v
10";
@@ -10,190 +10,191 @@ const DISCORD_APPLICAT
ION_ID = process.env.DISCORD_APPLICATION_ID;
 const DISCORD_BOT_TOKEN = process.env.DISCO
RD_BOT_TOKEN;
 
 if (!DISCORD_APPLICATION_ID || !DISCORD_BOT
_TOKEN) {
-	console.error("Error: DISCORD_APPLICATION_
ID and DISCORD_BOT_TOKEN must be set");
-	console.error("Please set these environmen
t variables and try again.");
-	process.exit(1);
+  console.error(
+    "Error: DISCORD_APPLICATION_ID and DISC
ORD_BOT_TOKEN must be set",
+  );
+  console.error("Please set these environme
nt variables and try again.");
+  process.exit(1);
 }
 
 // define commands
 const commands = [
-	// /player command
-	new SlashCommandBuilder()
-		.setName("player")
-		.setDescription("Look up a WoW Challenge 
Mode player")
-		.addStringOption((option) =>
-			option
-				.setName("name")
-				.setDescription("Player name")
-				.setRequired(true)
-				.setAutocomplete(true),
-		)
-		.addStringOption((option) =>
-			option
-				.setName("region")
-				.setDescription("Region")
-				.setRequired(true)
-				.addChoices(
-					{ name: "US", value: "us" },
-					{ name: "EU", value: "eu" },
-					{ name: "KR", value: "kr" },
-					{ name: "TW", value: "tw" },
-				),
-		)
-		.addStringOption((option) =>
-			option
-				.setName("realm")
-				.setDescription("Realm")
-				.setRequired(true)
-				.setAutocomplete(true),
-		),
+  // /player command
+  new SlashCommandBuilder()
+    .setName("player")
+    .setDescription("Look up a WoW Challeng
e Mode player")
+    .addStringOption((option) =>
+      option
+        .setName("name")
+        .setDescription("Player name")
+        .setRequired(true)
+        .setAutocomplete(true),
+    )
+    .addStringOption((option) =>
+      option
+        .setName("region")
+        .setDescription("Region")
+        .setRequired(true)
+        .setAutocomplete(true),
+    )
+    .addStringOption((option) =>
+      option
+        .setName("realm")
+        .setDescription("Realm")
+        .setRequired(true)
+        .setAutocomplete(true),
+    ),
 
-	// /leaderboard command
-	new SlashCommandBuilder()
-		.setName("leaderboard")
-		.setDescription("View WoW Challenge Mode 
leaderboards")
-		.addSubcommand((subcommand) =>
-			subcommand
-				.setName("dungeon")
-				.setDescription("View dungeon leaderboa
rd")
-				.addStringOption((option) =>
-					option
-						.setName("dungeon")
-						.setDescription("Choose a dungeon")
-						.setRequired(true)
-						.setAutocomplete(true),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("scope")
-						.setDescription("Leaderboard scope")
-						.setRequired(true)
-						.addChoices(
-							{ name: "Global", value: "global" },
-							{ name: "Region", value: "region" },
-							{ name: "Realm", value: "realm" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("region")
-						.setDescription("Region (required for
 regional/realm scope)")
-						.setRequired(false)
-						.addChoices(
-							{ name: "US", value: "us" },
-							{ name: "EU", value: "eu" },
-							{ name: "KR", value: "kr" },
-							{ name: "TW", value: "tw" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("realm")
-						.setDescription("Realm (required for 
realm scope)")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addIntegerOption((option) =>
-					option
-						.setName("limit")
-						.setDescription("Number of runs to di
splay (default: 10, max: 15)")
-						.setRequired(false)
-						.setMinValue(1)
-						.setMaxValue(15),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("season")
-						.setDescription("Season (default: cur
rent season)")
-						.setRequired(false),
-				),
-		)
-		.addSubcommand((subcommand) =>
-			subcommand
-				.setName("players")
-				.setDescription("View player rankings")
-				.addStringOption((option) =>
-					option
-						.setName("scope")
-						.setDescription("Leaderboard scope")
-						.setRequired(true)
-						.addChoices(
-							{ name: "Global", value: "global" },
-							{ name: "Region", value: "region" },
-							{ name: "Realm", value: "realm" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("region")
-						.setDescription("Region (required for
 regional/realm scope)")
-						.setRequired(false)
-						.addChoices(
-							{ name: "US", value: "us" },
-							{ name: "EU", value: "eu" },
-							{ name: "KR", value: "kr" },
-							{ name: "TW", value: "tw" },
-						),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("realm")
-						.setDescription("Realm (required for 
realm scope)")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("class")
-						.setDescription("Filter by class")
-						.setRequired(false)
-						.setAutocomplete(true),
-				)
-				.addIntegerOption((option) =>
-					option
-						.setName("limit")
-						.setDescription("Number of players to
 display (default: 25, max: 25)")
-						.setRequired(false)
-						.setMinValue(1)
-						.setMaxValue(25),
-				)
-				.addStringOption((option) =>
-					option
-						.setName("season")
-						.setDescription("Season (default: cur
rent season)")
-						.setRequired(false),
-				),
-		),
+  // /leaderboard command
+  new SlashCommandBuilder()
+    .setName("leaderboard")
+    .setDescription("View WoW Challenge Mod
e leaderboards")
+    .addSubcommand((subcommand) =>
+      subcommand
+        .setName("dungeon")
+        .setDescription("View dungeon leade
rboard")
+        .addStringOption((option) =>
+          option
+            .setName("dungeon")
+            .setDescription("Choose a dunge
on")
+            .setRequired(true)
+            .setAutocomplete(true),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("scope")
+            .setDescription("Leaderboard sc
ope")
+            .setRequired(true)
+            .addChoices(
+              { name: "Global", value: "glo
bal" },
+              { name: "Region", value: "reg
ion" },
+              { name: "Realm", value: "real
m" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("region")
+            .setDescription("Region (requir
ed for regional/realm scope)")
+            .setRequired(false)
+            .addChoices(
+              { name: "US", value: "us" },
+              { name: "EU", value: "eu" },
+              { name: "KR", value: "kr" },
+              { name: "TW", value: "tw" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("realm")
+            .setDescription("Realm (require
d for realm scope)")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addIntegerOption((option) =>
+          option
+            .setName("limit")
+            .setDescription("Number of runs
 to display (default: 10, max: 15)")
+            .setRequired(false)
+            .setMinValue(1)
+            .setMaxValue(15),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("season")
+            .setDescription("Season (defaul
t: current season)")
+            .setRequired(false),
+        ),
+    )
+    .addSubcommand((subcommand) =>
+      subcommand
+        .setName("players")
+        .setDescription("View player rankin
gs")
+        .addStringOption((option) =>
+          option
+            .setName("scope")
+            .setDescription("Leaderboard sc
ope")
+            .setRequired(true)
+            .addChoices(
+              { name: "Global", value: "glo
bal" },
+              { name: "Region", value: "reg
ion" },
+              { name: "Realm", value: "real
m" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("region")
+            .setDescription("Region (requir
ed for regional/realm scope)")
+            .setRequired(false)
+            .addChoices(
+              { name: "US", value: "us" },
+              { name: "EU", value: "eu" },
+              { name: "KR", value: "kr" },
+              { name: "TW", value: "tw" },
+            ),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("realm")
+            .setDescription("Realm (require
d for realm scope)")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("class")
+            .setDescription("Filter by clas
s")
+            .setRequired(false)
+            .setAutocomplete(true),
+        )
+        .addIntegerOption((option) =>
+          option
+            .setName("limit")
+            .setDescription(
+              "Number of players to display
 (default: 25, max: 25)",
+            )
+            .setRequired(false)
+            .setMinValue(1)
+            .setMaxValue(25),
+        )
+        .addStringOption((option) =>
+          option
+            .setName("season")
+            .setDescription("Season (defaul
t: current season)")
+            .setRequired(false),
+        ),
+    ),
 ].map((command) => command.toJSON());
 
 // register commands with Discord
 async function deployCommands() {
-	console.log("Registering Discord slash com
mands...");
-	console.log(`Application ID: ${DISCORD_APP
LICATION_ID}`);
-	console.log(`Commands to register: ${comma
nds.length}`);
+  console.log("Registering Discord slash co
mmands...");
+  console.log(`Application ID: ${DISCORD_AP
PLICATION_ID}`);
+  console.log(`Commands to register: ${comm
ands.length}`);
 
-	const rest = new REST({ version: "10" }).s
etToken(DISCORD_BOT_TOKEN);
+  const rest = new REST({ version: "10" }).
setToken(DISCORD_BOT_TOKEN);
 
-	try {
-		console.log("Sending registration request
 to Discord...");
+  try {
+    console.log("Sending registration reque
st to Discord...");
 
-		const data = await rest.put(
-			Routes.applicationCommands(DISCORD_APPLI
CATION_ID),
-			{ body: commands },
-		) as any[];
+    const data = (await rest.put(
+      Routes.applicationCommands(DISCORD_AP
PLICATION_ID),
+      { body: commands },
+    )) as any[];
 
-		console.log(`? Successfully registered ${
data.length} slash commands!`);
-		console.log("\nRegistered commands:");
-		data.forEach((cmd: any) => {
-			console.log(`  - /${cmd.name}: ${cmd.des
cription}`);
-		});
+    console.log(`? Successfully registered 
${data.length} slash commands!`);
+    console.log("\nRegistered commands:");
+    data.forEach((cmd: any) => {
+      console.log(`  - /${cmd.name}: ${cmd.
description}`);
+    });
 
-		console.log("\nYou can now use these comm
ands in Discord!");
-		console.log("Note: It may take a few minu
tes for commands to appear in all servers.")
;
-	} catch (error) {
-		console.error("? Error registering comman
ds:", error);
-		process.exit(1);
-	}
+    console.log("\nYou can now use these co
mmands in Discord!");
+    console.log(
+      "Note: It may take a few minutes for 
commands to appear in all servers.",
+    );
+  } catch (error) {
+    console.error("? Error registering comm
ands:", error);
+    process.exit(1);
+  }
 }
 
 // run the deployment
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET