import { option } from "@purrkit/router"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ContainerBuilder, SectionBuilder, TextDisplayBuilder, ThumbnailBuilder, } from "discord.js"; import { errorMessage, logger } from "@/lib"; import { useGetData } from "@/middleware"; import type { ApiErrorV2, ErrorCode } from "./types"; import { getThumbnail, getUser, getUsersByUsernames } from "./lib"; const ERROR_CODES: Set = new Set([ "INVALID_ARGUMENT", "PERMISSION_DENIED", "NOT_FOUND", "ABORTED", "RESOURCE_EXHAUSTED", "CANCELLED", "INTERNAL", "NOT_IMPLEMENTED", "UNAVAILABLE", ]); function isErrorCode(value: string): value is ErrorCode { return ERROR_CODES.has(value); } export function isApiErrorV2(x: unknown): x is ApiErrorV2 { return ( typeof x === "object" && x !== null && "code" in x && "message" in x && typeof (x as Record).code === "string" && typeof (x as Record).message === "string" && isErrorCode((x as Record).code as string) ); } export const robloxUserCommand = useGetData.command("user", { description: "Get a Roblox user's information", options: { username: option.string("The username of the user", { required: false }), id: option.string("The user ID of the user", { required: false }), }, run: async (interaction, args, { actor: _actor }) => { const { username, id: userId } = args; if (!username && !userId) return errorMessage(interaction, "You must provide either a username or user ID."); let id = userId; if (username && !id) { const [usernameData, usernameError] = await getUsersByUsernames([username]); if (usernameError || (usernameData && !usernameData.data[0])) return errorMessage(interaction, "There was an error fetching the user's ID."); id = usernameData.data[0]!.id; } if (!id) return errorMessage(interaction, "You must provide a user ID."); const [user, error] = await getUser(id); if (error) { logger.error(error, `Failed to fetch user ${id}`); return errorMessage(interaction, "There was an error fetching the user."); } const [thumbnail] = await getThumbnail(id); let thumbnailUrl = "https://images-ext-1.discordapp.net/external/j7H9Ep50cCN5igmEKGwFnv7frj2590Xg2Z4hvHGFPPo/%3Fsize%3D4096/https/cdn.discordapp.com/icons/895998762630127616/36c6e14d0933eb338826599b632df818.png?format=webp&quality=lossless&width=852&height=852"; if (thumbnail) thumbnailUrl = thumbnail.response.imageUri ?? thumbnailUrl; const date = Math.round(new Date(user.createTime).getTime() / 1000); const row = new ActionRowBuilder().addComponents( new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel("Open Profile") .setURL(`https://roblox.com/users/${id}/profile`), ); const userSection = new SectionBuilder() .addTextDisplayComponents( new TextDisplayBuilder().setContent(`# ${user.displayName || user.name}`), new TextDisplayBuilder().setContent( `@${user.name} - ${user.id} - created ()`, ), new TextDisplayBuilder().setContent( user.about ? `>>> ${user.about}` : ">>> No bio provided.", ), ) .setThumbnailAccessory(new ThumbnailBuilder().setURL(thumbnailUrl)); const container = new ContainerBuilder() .addSectionComponents(userSection) .addActionRowComponents(row); await interaction.reply(user.id); await interaction.followUp({ components: [container], flags: ["IsComponentsV2"], }); }, });