apps/bot/src/feats/roblox/user.ts (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
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<string> = new Set<ErrorCode>([
"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<string, unknown>).code === "string" &&
typeof (x as Record<string, unknown>).message === "string" &&
isErrorCode((x as Record<string, unknown>).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<ButtonBuilder>().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 <t:${date}> (<t:${date}:R>)`,
),
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"],
});
},
});
|