Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
url = https://github.com/Statsify/public-assets
[submodule "assets/private"]
path = assets/private
url = https://github.com/Statsify/assets
url = https://github.com/Statsify/assets
9 changes: 3 additions & 6 deletions apps/discord-bot/src/commands/arcade/arcade.command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,12 @@ export class ArcadeCommand extends BaseHypixelCommand<ArcadeModes> {
}

export function getArcadeModeEmojis(modes: GameModeWithSubModes<ArcadeModes>[]): ModeEmoji[] {
return modes.map((mode) => (t) => t(`emojis:arcade.${mode.api}`));
return modes.map(({ emoji }) => emoji ? (t) => t(emoji) : undefined);
}

export function getArcadeSubModeEmojis<M extends ApiModeFromGameModes<ArcadeModes>>(
mode: M,
_mode: M,
submodes: SubModeForMode<ArcadeModes, M>[]
): ModeEmoji[] {
if (mode === "zombies")
return submodes.map((submode) => (t) => t(`emojis:zombies.${submode.api}`));

return [];
return submodes.map(({ emoji }) => emoji ? (t) => t(emoji) : undefined);
}
8 changes: 6 additions & 2 deletions apps/discord-bot/src/commands/base.hypixel-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export interface ProfileData<T extends GamesWithBackgrounds, K = never> {
}

export type ModeEmoji = LocalizationString | false | undefined;
const metadataString = (key?: string): LocalizationString | undefined =>
key ? (t) => t(key) : undefined;

export interface BaseHypixelCommand<T extends GamesWithBackgrounds, K = never> {
getPreProfileData?(player: Player): K | Promise<K>;
Expand Down Expand Up @@ -89,7 +91,8 @@ export abstract class BaseHypixelCommand<T extends GamesWithBackgrounds, K = nev
const pages: Page[] = filteredModes.map((mode, index) => {
const pageInput = {
label: mode.formatted,
emoji: emojis[index],
description: metadataString(mode.description),
emoji: emojis[index] ?? metadataString(mode.emoji),
};

const filteredSubmodes = this.filterSubmodes?.(player, mode) ?? mode.submodes;
Expand Down Expand Up @@ -127,7 +130,8 @@ export abstract class BaseHypixelCommand<T extends GamesWithBackgrounds, K = nev

const subPages = filteredSubmodes.map((submode, index): SubPage => ({
label: submode.formatted,
emoji: submodeEmojis[index],
description: metadataString(submode.description),
emoji: submodeEmojis[index] ?? metadataString(submode.emoji),
generator: async (t) => {
const background = await getBackground(...mapBackground(this.modes, mode.api, submode.api as ApiSubModeForMode<T, (typeof mode)["api"]>));

Expand Down
13 changes: 10 additions & 3 deletions apps/discord-bot/src/commands/historical/session.command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
Command,
CommandContext,
EmbedBuilder,
LocalizationString,
Page,
PaginateService,
PlayerArgument,
Expand Down Expand Up @@ -86,6 +87,9 @@ import { render } from "@statsify/rendering";
import type { BaseProfileProps, ModeEmoji } from "#commands/base.hypixel-command";
import type { HistoricalTimeData } from "#components";

const metadataString = (key?: string): LocalizationString | undefined =>
key ? (t) => t(key) : undefined;

@Command({ description: "session stats" })
export class SessionCommand {
public constructor(
Expand Down Expand Up @@ -390,7 +394,8 @@ export class SessionCommand {

if (submodes.length === 0) return {
label: mode.formatted,
emoji: modeEmojis[index],
description: metadataString(mode.description),
emoji: modeEmojis[index] ?? metadataString(mode.emoji),
generator: async (t) => {
const background = await getBackground(...mapBackground(modes, mode.api));

Expand Down Expand Up @@ -438,7 +443,8 @@ export class SessionCommand {

const subPages = submodes.map((submode, index): SubPage => ({
label: submode.formatted,
emoji: submodeEmojis[index],
description: metadataString(submode.description),
emoji: submodeEmojis[index] ?? metadataString(submode.emoji),
generator: async (t) => {
const background = await getBackground(...mapBackground(modes, mode.api, submode.api as ApiSubModeForMode<T, (typeof mode)["api"]>));

Expand Down Expand Up @@ -473,7 +479,8 @@ export class SessionCommand {

return {
label: mode.formatted,
emoji: modeEmojis[index],
description: metadataString(mode.description),
emoji: modeEmojis[index] ?? metadataString(mode.emoji),
subPages,
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ import {
import { AbstractArgument, CommandContext, LocalizationString } from "@statsify/discord";
import {
ClassMetadata,
type GameModes,
LeaderboardScanner,
METADATA_KEY,
PlayerStats,
} from "@statsify/schemas";
import { removeFormatting } from "@statsify/util";

type LeaderboardChoice = APIApplicationCommandOptionChoice & { aliases?: string[] };

const entries = Object.entries(
Reflect.getMetadata(METADATA_KEY, PlayerStats.prototype) as ClassMetadata
);

const FUSE_OPTIONS = {
keys: ["name", "key"],
keys: ["name", "value", "aliases"],
includeScore: false,
shouldSort: true,
isCaseSensitive: false,
Expand All @@ -41,7 +44,7 @@ const fields = entries.reduce((acc, [prefix, value]) => {
const fuse = new Fuse(list, FUSE_OPTIONS);

return { ...acc, [prefix]: [fuse, list] };
}, {} as Record<keyof PlayerStats, [Fuse<APIApplicationCommandOptionChoice>, APIApplicationCommandOptionChoice[]]>);
}, {} as Record<keyof PlayerStats, [Fuse<LeaderboardChoice>, LeaderboardChoice[]]>);

export class PlayerLeaderboardArgument extends AbstractArgument {
public name = "leaderboard";
Expand All @@ -50,23 +53,56 @@ export class PlayerLeaderboardArgument extends AbstractArgument {
public required = true;
public autocomplete = true;

public constructor(private prefix: keyof PlayerStats) {
private fuse: Fuse<LeaderboardChoice>;
private list: LeaderboardChoice[];

public constructor(private prefix: keyof PlayerStats, modes?: GameModes<any>) {
super();
this.description = (t) => t("arguments.player-leaderboard");

const [, baseList] = fields[this.prefix];
this.list = modes ?
baseList.map((choice) => ({
...choice,
aliases: this.getAliases(choice.value as string, modes),
})) :
baseList;

this.fuse = new Fuse(this.list, FUSE_OPTIONS);
}

public autocompleteHandler(
context: CommandContext
): APIApplicationCommandOptionChoice[] {
const currentValue = context.option<string>(this.name, "").toLowerCase();

const [fuse, list] = fields[this.prefix];
if (!currentValue) return this.toChoices(this.list.slice(0, 25));

return this.toChoices(
this.fuse
.search(currentValue)
.map((result) => result.item)
.slice(0, 25)
);
}

if (!currentValue) return list.slice(0, 25);
private getAliases(key: string, modes: GameModes<any>): string[] {
const [modeKey, submodeKey] = key.split(".");
const aliases = new Set<string>();

const mode = modes.getModes().find(({ api }) => api === modeKey);

if (!mode) return [];

mode.aliases.forEach((alias: string) => aliases.add(alias));
mode.submodes
.find(({ api }) => api === submodeKey)
?.aliases.forEach((alias: string) => aliases.add(alias));

return [...aliases];
}

return fuse
.search(currentValue)
.map((result) => result.item)
.slice(0, 25);
private toChoices(choices: LeaderboardChoice[]): APIApplicationCommandOptionChoice[] {
return choices.map(({ name, value }) => ({ name, value }));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class PlayerLeaderboardCommand extends BaseLeaderboardCommand {

@SubCommand({
description: (t) => t("commands.leaderboard-arcade"),
args: [new PlayerLeaderboardArgument("arcade")],
args: [new PlayerLeaderboardArgument("arcade", ARCADE_MODES)],
})
public arcade(context: CommandContext) {
return this.run(context, "arcade", ARCADE_MODES);
Expand Down
5 changes: 5 additions & 0 deletions apps/discord-bot/src/commands/ratios/ratios.command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
ApiService,
Command,
CommandContext,
LocalizationString,
Page,
PaginateService,
PlayerArgument,
Expand All @@ -56,6 +57,8 @@ import { getTheme } from "#themes";
import { render } from "@statsify/rendering";

const args = [PlayerArgument];
const metadataString = (key?: string): LocalizationString | undefined =>
key ? (t) => t(key) : undefined;

@Command({ description: (t) => t("commands.ratios") })
export class RatiosCommand {
Expand Down Expand Up @@ -208,6 +211,8 @@ export class RatiosCommand {

const pages: Page[] = displayedModes.map((mode, index) => ({
label: mode.formatted,
description: metadataString(mode.description),
emoji: metadataString(mode.emoji),
generator: async (t) => {
const background = await getBackground(...mapBackground(modes, mode.api));

Expand Down
40 changes: 40 additions & 0 deletions locales/en-US/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,46 @@
},
"commands": {
"arcade": "$t(commands.hypixel-command, { \"name\": \"Arcade\" })",
"options": {
"arcade": {
"overall": "Overall Arcade stats",
"blockingDead": "Blocking Dead stats",
"bountyHunters": "Bounty Hunters stats",
"creeperAttack": "Creeper Attack stats",
"disasters": "Disasters stats",
"dragonWars": "Dragon Wars stats",
"dropper": "Dropper stats",
"enderSpleef": "Ender Spleef stats",
"farmHunt": "Farm Hunt stats",
"football": "Football stats",
"galaxyWars": "Galaxy Wars stats",
"hideAndSeek": "Hide and Seek stats",
"holeInTheWall": "Hole in the Wall stats",
"hypixelSays": "Hypixel Says stats",
"miniWalls": "Mini Walls stats",
"partyGames": "Party Games stats",
"pixelPainters": "Pixel Painters stats",
"pixelParty": "Pixel Party stats",
"seasonal": "Seasonal Arcade stats",
"throwOut": "Throw Out stats",
"zombies": "Zombies stats",
"submodes": {
"overall": "Overall stats",
"survivals": "Survivals stats",
"deaths": "Deaths stats",
"bestTimes": "Best time stats",
"completions": "Completion stats",
"roundWins": "Round win stats",
"zombies": {
"overall": "Overall Zombies stats",
"deadEnd": "Dead End stats",
"badBlood": "Bad Blood stats",
"alienArcadium": "Alien Arcadium stats",
"prison": "Prison stats"
}
}
}
},
"arenabrawl": "$t(commands.hypixel-command, { \"name\": \"Arena Brawl\" })",
"available": "Check the availability of a Minecraft username",
"badge": "Change your badge for every profile",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class SelectMenuOptionBuilder {
return {
label: translateField(locale, this.#label),
value: this.#value,
description: translateField(locale, this.#description),
description: this.#description ? translateField(locale, this.#description) : undefined,
emoji: this.#emoji ? parseEmoji(this.#emoji, locale) : undefined,
default: this.#defaultValue,
};
Expand Down
20 changes: 11 additions & 9 deletions packages/discord/src/services/paginate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ type PaginateInteractionContentGenerator = (
export type Page = PageInput & ({ subPages: SubPage[] } | { generator: PaginateInteractionContentGenerator });
export type SubPage = PageInput & { generator: PaginateInteractionContentGenerator };

interface PageInput {
label: LocalizationString;
emoji?: LocalizationString | false;
}
interface PageInput {
label: LocalizationString;
description?: LocalizationString;
emoji?: LocalizationString | false;
}

type PageId = `${number}|${number}`;

Expand Down Expand Up @@ -245,11 +246,12 @@ class PageController {
if (pages.length > 5) {
const menu = new SelectMenuBuilder();

pages.forEach((page, index) => {
const option = new SelectMenuOptionBuilder().label(page.label).value(`${index}`);
if (page.emoji) option.emoji(page.emoji);
menu.option(option);
});
pages.forEach((page, index) => {
const option = new SelectMenuOptionBuilder().label(page.label).value(`${index}`);
if (page.description) option.description(page.description);
if (page.emoji) option.emoji(page.emoji);
menu.option(option);
});

menu.activeOption(selected);
this.#menu = menu;
Expand Down
Loading
Loading