26 options: [
27 {
28 name: "reactions",
29 description: "Manage the role reactions on your server.",
30 type: ApplicationCommandOptionTypes.SubCommandGroup,
31 options: [
32 {
33 name: "create",
34 description: "Create a reaction role on your server.",
35 type: ApplicationCommandOptionTypes.SubCommand,
36 options: [
75 ],
76 async execute(interaction, args: CommandArgs) {
77 // Create a reaction role
78 if (args.reactions?.create) {
79 // Ensure that there is a channelId
80 if (!interaction.channelId) {
85 }
86
87 // This array is used to store all the roles for this reaction roles
88 let roles = [args.reactions.create];
89
90 // Send the message that uses will use to get the role
122 collectors.add(itemCollector);
123
124 // For the new reaction role, we need to keep track of what the user gave us
125 let partialRoleInfo: Partial<(typeof roles)[number]> | undefined;
126
132
133 // Save button
134 if (i.data?.customId === "reactionRoles-save") {
135 // Remove this item collector from the list of collectors (we aren't correcting anymore)
136 collectors.delete(itemCollector);
144
145 // New button
146 if (i.data?.customId === "reactionRoles-add") {
147 partialRoleInfo = {};
148
149 // Ask the user for the role
150 await i.edit({
151 content: "Pick a role for the new reaction role",
152 components: [selectRoleActionRow],
153 });
156
157 // New button - role select menu
158 if (partialRoleInfo && i.data?.customId === "reactionRoles-add-role") {
159 const roleToAdd = i.data?.resolved?.roles?.first();
160
171 // Ask the user for the color of the button
172 await i.edit({
173 content: "Pick a color for the reaction role",
174 components: [selectColorActionRow],
175 });
179
180 // New button - color select menu
181 if (partialRoleInfo && i.data?.customId === "reactionRoles-add-color") {
182 const color = parseInt(i.data?.values?.[0] ?? "NaN");
183
194 // Ask the user to input the emoji and optionally a label for the button
195 await i.respond({
196 title: "Pick an emoji and label for the reaction role",
197 components: [selectEmojiActionRow, selectLabelActionRow],
198 customId: "reactionRoles-add-modal",
199 });
200
203
204 // New button - emoji & label modal
205 if (partialRoleInfo && i.data?.customId === "reactionRoles-add-modal") {
206 // Ensure that we can get the channelId from the interaction
207 if (!interaction.channelId) {
254 // Respond to the modal. A modal submit (type 5) interaction can't edit the original response
255 await i.respond(
256 "Reaction role created successfully. You can use the message above to add/remove a role",
257 { isPrivate: true },
258 );
262
263 // Remove button
264 if (i.data?.customId === "reactionRoles-remove") {
265 // Clone the actionRow for the remove select menu, this is to prevent unwanted data to appear to other users
266 const removeActionRow = structuredClone(removeActionRowTemplate);
276 }
277
278 // Ask the user for what reaction role they want to remove
279 await i.edit({
280 content: "Select what reaction role to remove",
281 components: [removeActionRow],
282 });
286
287 // Remove button - role select menu
288 if (i.data?.customId === "reactionRoles-remove-selectMenu") {
289 // Ensure that we can get the channelId from the interaction
290 if (!interaction.channelId) {
342// Interface to type the arguments that we receive from discord
343interface CommandArgs {
344 reactions?: {
345 create?: {
346 role: typeof bot.transformers.$inferredTypes.role;
360 type: MessageComponentTypes.Button,
361 style: ButtonStyles.Success,
362 customId: "reactionRoles-add",
363 emoji: {
364 name: "➕",
370 type: MessageComponentTypes.Button,
371 style: ButtonStyles.Danger,
372 customId: "reactionRoles-remove",
373 emoji: {
374 name: "➖",
380 type: MessageComponentTypes.Button,
381 style: ButtonStyles.Success,
382 customId: "reactionRoles-save",
383 emoji: {
384 name: "✅",
394 {
395 type: MessageComponentTypes.SelectMenu,
396 customId: "reactionRoles-remove-selectMenu",
397 maxValues: 1,
398 minValues: 1,
408 {
409 type: MessageComponentTypes.SelectMenuRoles,
410 customId: "reactionRoles-add-role",
411 maxValues: 1,
412 minValues: 1,
421 {
422 type: MessageComponentTypes.SelectMenu,
423 customId: "reactionRoles-add-color",
424 options: [
425 { label: "Blue", value: ButtonStyles.Primary.toString() },
438 type: MessageComponentTypes.InputText,
439 style: TextStyles.Short,
440 customId: "reactionRoles-add-emoji",
441 label: "Emoji for the reaction role",
442 required: true,
443 },
451 type: MessageComponentTypes.InputText,
452 style: TextStyles.Short,
453 customId: "reactionRoles-add-label",
454 label: "Label for the reaction role [OPTIONAL]",
455 // Discord imposed limit for button labels
456 maxLength: 80,
459} as const;
460
461// Function to get all the actionRows with buttons for the reaction roles message
462function getRoleButtons(
463 roles: Array<{
504 },
505 label: roleInfo.label,
506 customId: `reactionRoles-role-${roleInfo.role.id}`,
507 });
508 }