Get started with Peachy now 🍑
PEACHYPEACHY
Rank

Leaderboard

The `leaderboard` command displays rankings for various metrics within a Discord guild and globally, such as coin balance, spent coins, and streaks for specific activities. It can be triggered using the prefixes `P`, `p`, or the slash command `/`.

Command Details

  • Name: leaderboard
  • Aliases: rank, top, lb
  • Category: Rank
  • Description: Displays top coin, spent coin, and streak rankings in your Discord guild and globally.
  • Usage: P leaderboard [type], p leaderboard [type], or /leaderboard [type]
  • Examples:
    • P leaderboard bal
    • p leaderboard peach
    • /leaderboard slots
  • Cooldown: 3 seconds
  • Arguments: Optional (defaults to "bal")
  • Slash Command: Enabled
  • Permissions:
    • Bot: SendMessages, ViewChannel, EmbedLinks
    • User: None
  • Player Requirements: None (no voice, DJ, or active player required)

Functionality

  • If no type is specified, the command defaults to showing the "balance" leaderboard.
  • The command fetches user rankings based on the specified type (e.g., balance, peach, slots) using client.utils.userRanking.
  • It displays up to 100 users in a paginated embed, with each page showing 10 users. Each entry includes:
    • Position with an emoji rank indicator (e.g., 🥇 for 1st).
    • Username (or "Unknown" if not available).
    • Total value for the selected metric (formatted number) with a relevant emoji.
  • Pagination is handled via client.utils.reactionPaginate, allowing users to navigate through pages.
  • If no users are found for the leaderboard, an error message is displayed using client.utils.oops.
  • Supports both prefix-based (P or p) and slash command (/) inputs.
  • Uses language localization for titles, messages, and emojis.

Options

The command accepts a type option to specify which leaderboard to display:

NameValueDescription
BalancebalanceDisplays coin balance ranking
PeachpeachDisplays peach streak ranking
GomagomaDisplays goma streak ranking
SlotsslotsDisplays slots streak ranking
BlackjackblackjackDisplays blackjack ranking
CoinflipcoinflipDisplays coinflip ranking
SponsorsponsorDisplays sponsor ranking

Code Overview

The command is built using a Command class structure and handles both message-based and interaction-based (slash command) inputs. It uses:

  • client.utils.userRanking to fetch ranking data.
  • client.embed() to create formatted embeds for each page.
  • client.utils.chunk to split the leaderboard into pages of 10 users.
  • client.utils.reactionPaginate for pagination navigation.
  • client.utils.emojiRank and client.utils.typeRanking for dynamic emoji display.
  • Language localization for messages, titles, and footers.
  • Error handling for cases where no users are found.
const { Command } = require("../../structures");

module.exports = class Ranking extends Command {
  constructor(client) {
    super(client, {
      name: "leaderboard",
      description: {
        content:
          "Check top coin, spent coin, and streak of peachy in your Discord guild and globally.",
        examples: ["leaderboard bal", "leaderboard peach", "leaderboard slots"],
        usage: "leaderboard bal\nrank bal\ntop bal\nlb bal",
      },
      category: "rank",
      aliases: ["rank", "top", "lb"],
      cooldown: 3,
      args: false,
      permissions: {
        dev: false,
        client: ["SendMessages", "ViewChannel", "EmbedLinks"],
        user: [],
      },
      slashCommand: true,
      options: [
        {
          name: "type",
          type: 3,
          description: "Choose which leaderboard to display",
          required: true,
          choices: [
            { name: "Balance", value: "balance" },
            { name: "Peach", value: "peach" },
            { name: "Goma", value: "goma" },
            { name: "Slots", value: "slots" },
            { name: "Blackjack", value: "blackjack" },
            { name: "Coinflip", value: "coinflip" },
            { name: "Sponsor", value: "sponsor" },
          ],
        },
      ],
    });
  }

  async run(client, ctx, args, color, emoji, language) {
    const generalMessages = language.locales.get(
      language.defaultLocale
    )?.generalMessages;
    const rankingMessages = language.locales.get(language.defaultLocale)
      ?.economyMessages?.rankingMessages;
    const type = ctx.isInteraction
      ? ctx.interaction.options.data[0]?.value
      : args[0] || "bal";
    const users = await client.utils.userRanking(client, ctx, color, type);

    if (!users.length) {
      return await client.utils.oops(
        client,
        ctx,
        rankingMessages.notFound,
        color
      );
    }

    const leaderboardList = users.slice(0, 100).map((user, index) => {
      const position = index + 1;
      const emojiRank = client.utils.emojiRank(emoji, position);
      return `**${emojiRank} ${position}. ${
        user.username || "Unknown"
      }**\n**${client.utils.formatNumber(
        user.total ?? 0
      )}** ${client.utils.typeRanking(type, emoji)}`;
    });

    const chunks = client.utils.chunk(leaderboardList, 10);
    const pages = chunks.map((chunk, i) => {
      return client
        .embed()
        .setColor(color.main)
        .setDescription(
          generalMessages.title
            .replace("%{mainLeft}", emoji.rank.owner)
            .replace(
              "%{title}",
              client.utils.titleRanking(type, rankingMessages)
            )
            .replace("%{mainRight}", emoji.rank.owner) + `${chunk.join("\n\n")}`
        )
        .setFooter({
          text: `${generalMessages.requestedBy.replace(
            "%{username}",
            ctx.author.displayName
          )} | Page ${i + 1} of ${chunks.length}`,
          iconURL: ctx.author.displayAvatarURL(),
        });
    });
    return await client.utils.reactionPaginate(ctx, pages);
  }
};