feat: fetch user info
vi did:web:vt3e.cat
Mon, 25 May 2026 09:10:25 +0100
3 files changed,
40 insertions(+),
36 deletions(-)
M
src/commands/ban.ts
→
src/commands/ban.ts
@@ -14,15 +14,10 @@ };
function renderGameButtons(state: State, banningIn: Game[]): ActionRowBuilder<ButtonBuilder> { const { successfulBans, failedBans } = state; - const allBans = [...successfulBans, ...failedBans]; const successfulIds = new Set(successfulBans.map((b) => b.id)); const failedIds = new Set(failedBans.map((b) => b.id)); const pendingIds = new Set(banningIn.map((b) => b.id)); - const allBanIds = new Set(allBans.map((b) => b.id)); - const excludedGameIds = new Set( - GAMES.filter((game) => !allBanIds.has(game.id)).map((game) => game.id), - ); const row = new ActionRowBuilder<ButtonBuilder>();@@ -67,7 +62,7 @@ builder
.setName("ban") .setDescription("ban a player") .addStringOption((option) => - option.setName("player").setDescription("the player to ban").setRequired(true), + option.setName("player").setDescription("the ID of the player to ban").setRequired(true), ) .addStringOption((option) => option.setName("reason").setDescription("the reason for the ban").setRequired(true),@@ -110,7 +105,6 @@ length: interaction.options.getString("length", false),
banAlts: interaction.options.getBoolean("ban-alts", false), includedGames: interaction.options.getString("games", false), }; - const id = options.player.startsWith("id:") ? options.player.slice(3) : options.player; const gamesResult = Result.from(() => parseIncludedGames(options.includedGames)); if (gamesResult.isErr()) {@@ -122,7 +116,7 @@ return;
} const games = gamesResult.unwrap(); - const user = await getUser(id); + const user = await getUser(options.player); const duration = options.length ? parseDuration(options.length).toString() : undefined; const extraButtons = new ActionRowBuilder<ButtonBuilder>().addComponents(@@ -148,12 +142,11 @@ ].join("\n");
const state = createReactive<State>( { successfulBans: [], failedBans: [], prefix: "⏳ Banning" }, - (updatedState) => { - this.container.logger.debug("state updated", updatedState); + (_newState) => { const gameButtons = renderGameButtons(state, games); const userParts = [ - `**@${user.username}**`, + `**@${user.name}**`, user.displayName ? `(${user.displayName})` : null, `id:${user.id}`, ];@@ -173,7 +166,9 @@ };
await Promise.all( games.map(async (game) => { - const banResult = await Result.fromAsync(() => ban(id, game.universeId, banOptions)); + const banResult = await Result.fromAsync(() => + ban(user.id.toString(), game.universeId, banOptions), + ); if (banResult.isOk()) state.successfulBans.push(game); else state.failedBans.push(game); }),
M
src/roblox/index.ts
→
src/roblox/index.ts
@@ -1,36 +1,24 @@
import { container } from "@sapphire/framework"; import config from "@/config"; +import type { BanResponse, RobloxUser } from "./types"; +const COOKIE = config.roblox.cookie; let CSRF_TOKEN: string | undefined; -export type RobloxUser = { - username: string; - displayName: string | null; - id: string; -}; +const request = (url: URL, options?: RequestInit) => + fetch(url, { + headers: { + Cookie: COOKIE, + }, + ...options, + }); export async function getUser(id: string): Promise<RobloxUser> { - const includeDisplayName = Math.random() < 0.5; + const url = new URL(`https://users.roblox.com/v1/users/${id}`); - return { - username: "stub", - displayName: includeDisplayName ? "stubDisplay" : null, - id: id, - }; + const response = await request(url); + return (await response.json()) as RobloxUser; } - -export type BanResponse = { - path: string; - user: string; - gameJoinRestriction: { - active: boolean; - startTime: string; - privateReason: string; - displayReason: string; - excludeAltAccounts: boolean; - inherited: boolean; - }; -}; export async function ban( userId: string,@@ -89,3 +77,5 @@ };
return await makeRequest(); } + +export * from "./types";
A
src/roblox/types.ts
@@ -0,0 +1,19 @@
+export type RobloxUser = { + id: number; + name: string; + displayName: string | null; + created: string; +}; + +export type BanResponse = { + path: string; + user: string; + gameJoinRestriction: { + active: boolean; + startTime: string; + privateReason: string; + displayReason: string; + excludeAltAccounts: boolean; + inherited: boolean; + }; +};