ItsMyStudio

Leaderboards

Create addon leaderboards that plug into the built-in `/leaderboard` command and expose ranked entries to the core leaderboard service.

Support

Use leaderboards/ when your addon should expose ranked data inside the built-in /leaderboard command.

What it is

A leaderboard is a ranked dataset registered under a name.

Once registered, the core bot adds it as a subcommand under /leaderboard.

Typical examples:

  • top message senders
  • top economy balances
  • top ticket helpers
  • top inviters

Where to create it

messages.ts
invites.ts

Create one file per leaderboard source.

What it adds

This folder adds:

  • a new /leaderboard <name> subcommand
  • a ranked dataset the core command can paginate
  • placeholder access through the leaderboard expansion

The important point is that your addon does not build the whole message output anymore. It only returns ranked entries and, optionally, how the numeric value should be formatted.

How to create it

Rules:

  • each file should export one default class
  • that class must extend Leaderboard<TAddon>

Base leaderboard API

Prop

Type

LeaderboardEntry shape

getData() should return objects with this shape:

Prop

Type

Minimal example

import { Leaderboard, LeaderboardEntry, User } from '@itsmybot';
import { Op } from 'sequelize';
import MyAddon from '..';

export default class MessagesLeaderboard extends Leaderboard<MyAddon> {
  name = 'messages';
  description = 'Messages leaderboard.';

  async getData(): Promise<LeaderboardEntry[]> {
    const users = await User.findAll({
      order: [['messages', 'DESC']],
      where: {
        messages: {
          [Op.gt]: 0,
        },
        isBot: false,
      },
    });

    return users.map((user, index) => ({
      position: index + 1,
      userId: user.id,
      value: user.messages,
    }));
  }
}

Example with custom formatting

If the raw number is not enough, override formatValue(entry).

import { Leaderboard, LeaderboardEntry, Utils, Variable } from '@itsmybot';
import MyAddon from '..';

interface InviteEntry {
  regular: number;
  left: number;
  fake: number;
  bonus: number;
}

export default class InvitesLeaderboard extends Leaderboard<MyAddon> {
  name = 'invites';
  description = 'Invites leaderboard.';

  async getData(): Promise<LeaderboardEntry<InviteEntry>[]> {
    return [
      {
        position: 1,
        userId: '123456789012345678',
        value: 24,
        additionalData: {
          regular: 18,
          left: 2,
          fake: 1,
          bonus: 3,
        },
      },
    ];
  }

  async formatValue(entry: LeaderboardEntry<InviteEntry>) {
    const variables: Variable[] = [
      { name: 'value', value: entry.value },
      { name: 'regular', value: entry.additionalData?.regular },
      { name: 'left', value: entry.additionalData?.left },
      { name: 'fake', value: entry.additionalData?.fake },
      { name: 'bonus', value: entry.additionalData?.bonus },
    ];

    return Utils.applyVariables(
      '**[[value]]** invites ([[regular]] regular, [[left]] left, [[fake]] fake, [[bonus]] bonus)',
      variables,
    );
  }
}

What the core service handles for you

Once your leaderboard is registered, the core service handles:

  • command registration under /leaderboard
  • caching the result for 60 seconds
  • pagination
  • title, entry, and footer rendering
  • user lookup from entry.userId
  • calling formatValue(entry) when needed

That means your addon only owns the dataset and the optional value formatter.

Leaderboard placeholders

Registered leaderboards are also available through the built-in leaderboard expansion.

Examples:

%leaderboard_messages_top_1_user%
%leaderboard_messages_top_1_value%
%leaderboard_messages_top_1_value_formatted%
%leaderboard_messages_top_1_user_mention%

Useful patterns:

  • top_<position>_user
  • top_<position>_value
  • top_<position>_value_formatted
  • top_<position>_user_<userVariable>

So if your addon registers a leaderboard called invites, addon users can immediately reuse it in placeholders too.

On this page