diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index bfda294..2e1ab9c 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -1,8 +1,8 @@ import { createCommand } from "@/lib/utils"; import { SlashCommandBuilder } from "discord.js"; import { DrizzleClient } from "@/lib/DrizzleClient"; -import { users } from "@/db/schema"; -import { desc } from "drizzle-orm"; +import { users, items, inventory } from "@/db/schema"; +import { desc, sql, eq } from "drizzle-orm"; import { createWarningEmbed } from "@lib/embeds"; import { getLeaderboardEmbed } from "@/modules/leveling/leveling.view"; @@ -12,30 +12,49 @@ export const leaderboard = createCommand({ .setDescription("View the top players") .addStringOption(option => option.setName("type") - .setDescription("Sort by XP or Balance") + .setDescription("Sort by XP, Balance, or Net Worth") .setRequired(true) .addChoices( { name: "Level / XP", value: "xp" }, - { name: "Balance", value: "balance" } + { name: "Balance", value: "balance" }, + { name: "Net Worth", value: "networth" } ) ), execute: async (interaction) => { await interaction.deferReply(); const type = interaction.options.getString("type", true); - const isXp = type === "xp"; - const leaders = await DrizzleClient.query.users.findMany({ - orderBy: isXp ? desc(users.xp) : desc(users.balance), - limit: 10 - }); + let leaders; + + if (type === 'networth') { + leaders = await DrizzleClient.select({ + username: users.username, + level: users.level, + xp: users.xp, + balance: users.balance, + netWorth: sql`${users.balance} + COALESCE(SUM(${items.price} * ${inventory.quantity}), 0)`.as('net_worth') + }) + .from(users) + .leftJoin(inventory, eq(users.id, inventory.userId)) + .leftJoin(items, eq(inventory.itemId, items.id)) + .groupBy(users.id) + .orderBy(desc(sql`net_worth`)) + .limit(10); + } else { + const isXp = type === "xp"; + leaders = await DrizzleClient.query.users.findMany({ + orderBy: isXp ? desc(users.xp) : desc(users.balance), + limit: 10 + }); + } if (leaders.length === 0) { await interaction.editReply({ embeds: [createWarningEmbed("No users found.", "Leaderboard")] }); return; } - const embed = getLeaderboardEmbed(leaders, isXp ? 'xp' : 'balance'); + const embed = getLeaderboardEmbed(leaders, type as 'xp' | 'balance' | 'networth'); await interaction.editReply({ embeds: [embed] }); } diff --git a/src/modules/leveling/leveling.view.ts b/src/modules/leveling/leveling.view.ts index 5140f35..008ded1 100644 --- a/src/modules/leveling/leveling.view.ts +++ b/src/modules/leveling/leveling.view.ts @@ -8,6 +8,7 @@ interface LeaderboardUser { level: number | null; xp: bigint | null; balance: bigint | null; + netWorth?: bigint | null; } /** @@ -23,23 +24,45 @@ function getMedalEmoji(index: number): string { /** * Formats a single leaderboard entry based on type */ -function formatLeaderEntry(user: LeaderboardUser, index: number, type: 'xp' | 'balance'): string { +function formatLeaderEntry(user: LeaderboardUser, index: number, type: 'xp' | 'balance' | 'networth'): string { const medal = getMedalEmoji(index); - const value = type === 'xp' - ? `Lvl ${user.level ?? 1} (${user.xp ?? 0n} XP)` - : `${user.balance ?? 0n} 🪙`; + let value = ''; + + switch (type) { + case 'xp': + value = `Lvl ${user.level ?? 1} (${user.xp ?? 0n} XP)`; + break; + case 'balance': + value = `${user.balance ?? 0n} 🪙`; + break; + case 'networth': + value = `${user.netWorth ?? 0n} 🪙 (Net Worth)`; + break; + } + return `${medal} **${user.username}** — ${value}`; } /** - * Creates a leaderboard embed for either XP or Balance rankings + * Creates a leaderboard embed for either XP, Balance or Net Worth rankings */ -export function getLeaderboardEmbed(leaders: LeaderboardUser[], type: 'xp' | 'balance'): EmbedBuilder { +export function getLeaderboardEmbed(leaders: LeaderboardUser[], type: 'xp' | 'balance' | 'networth'): EmbedBuilder { const description = leaders.map((user, index) => formatLeaderEntry(user, index, type) ).join("\n"); - const title = type === 'xp' ? "🏆 XP Leaderboard" : "💰 Richest Players"; + let title = ''; + switch (type) { + case 'xp': + title = "🏆 XP Leaderboard"; + break; + case 'balance': + title = "💰 Richest Players"; + break; + case 'networth': + title = "💎 Net Worth Leaderboard"; + break; + } return new EmbedBuilder() .setTitle(title)