src/interactions/commands/search.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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import {
ActionRowBuilder,
ButtonBuilder,
type ButtonInteraction,
ButtonStyle,
type ChatInputCommandInteraction,
type Client,
ContainerBuilder,
MediaGalleryBuilder,
MediaGalleryItemBuilder,
SlashCommandBuilder,
TextDisplayBuilder,
escapeMarkdown,
} from "discord.js";
import config from "@/config";
import { getRobloxUser, searchRobloxUsers } from "@/utils/roblox";
import { formatUserInfo } from "@/utils/userInfo";
const commandData = new SlashCommandBuilder()
.setName("search")
.setDescription("search for roblox users")
.addStringOption((option) =>
option
.setName("query")
.setDescription("the username to search for")
.setRequired(true),
)
.addIntegerOption((option) =>
option
.setName("limit")
.setDescription("number of results to show (1-25)")
.setMinValue(1)
.setMaxValue(25)
.setRequired(false),
);
async function execute(
_client: Client,
interaction: ChatInputCommandInteraction,
) {
const query = interaction.options.getString("query", true);
const limit = interaction.options.getInteger("limit") || 10;
await interaction.deferReply();
const {
users: usersResult,
error,
code,
} = await searchRobloxUsers(query, limit);
if (error) {
if (code === 429)
return interaction.editReply({
content: "❌ ran into a rate limit, wait a minute or so and retry",
});
return interaction.editReply({
content: `❌ error while searching: ${error}`,
});
}
if (usersResult.length === 0) {
await interaction.editReply({
content: `❌ no users found for "${query}"`,
});
return;
}
const userContainers = Promise.all(
usersResult.map(async (user) => {
const userId = user.id;
const userResult = await getRobloxUser(String(userId), 75);
if (!userResult) return null;
const container = new ContainerBuilder().setAccentColor([203, 166, 247]);
const avatarParent = new MediaGalleryBuilder();
if (userResult.thumbnail.done) {
const avatar = new MediaGalleryItemBuilder().setURL(
userResult.thumbnail.response.imageUri,
);
avatarParent.addItems(avatar);
}
const title = new TextDisplayBuilder().setContent(
`## ${userResult.user.name} (${userResult.user.id})`,
);
const description = new TextDisplayBuilder().setContent(
escapeMarkdown(userResult.user.about || "no description provided"),
);
const profileLink = new ButtonBuilder()
.setLabel("view profile")
.setStyle(ButtonStyle.Link)
.setURL(`https://www.roblox.com/users/${userResult.user.id}/profile`);
const showMore = new ButtonBuilder()
.setLabel("show more")
.setStyle(ButtonStyle.Primary)
.setCustomId(`search:${userResult.user.id}`);
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
profileLink,
showMore,
);
container.addMediaGalleryComponents(avatarParent);
container.addTextDisplayComponents(title, description);
container.addActionRowComponents(actionRow);
return container;
}),
);
const usersData = await userContainers;
const nonNull = usersData.filter((userData) => userData !== null);
await interaction.followUp({
components: nonNull,
flags: ["IsComponentsV2"],
});
}
async function buttonExecute(_client: Client, interaction: ButtonInteraction) {
if (!interaction.isButton()) return;
const [action, userId] = interaction.customId.split(":");
if (action !== "search") return;
const { user, thumbnail } = await getRobloxUser(userId, 420);
const { embed } = formatUserInfo(
user,
thumbnail.done ? thumbnail.response.imageUri : null,
);
await interaction.reply({ embeds: [embed], flags: ["Ephemeral"] });
}
export default {
enabled: true,
data: commandData,
execute,
buttonExecute,
};
|