Leaderboards
Create addon leaderboards that plug into the built-in `/leaderboard` command and expose ranked entries to the core leaderboard service.
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
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
leaderboardexpansion
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>_usertop_<position>_valuetop_<position>_value_formattedtop_<position>_user_<userVariable>
So if your addon registers a leaderboard called invites, addon users can immediately reuse it in placeholders too.