Skip to content

Commit 135b614

Browse files
committed
Change Discord interaction to select menu
The Discord button system works for up to 5 buttons, so more than 5 CTFs will crash the code. See: Discord bot supports up to 5 CTFs, crashes otherwise TFNS#408 Therefore we change it to select dropdowns to support an arbitrary amount.
1 parent 7f81660 commit 135b614

File tree

6 files changed

+78
-59
lines changed

6 files changed

+78
-59
lines changed

api/src/discord/agile/commands/archiveCtf.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {
22
ActionRowBuilder,
33
ApplicationCommandType,
4-
ButtonBuilder,
5-
ButtonInteraction,
6-
ButtonStyle,
74
Client,
85
CommandInteraction,
96
PermissionFlagsBits,
7+
StringSelectMenuBuilder,
8+
StringSelectMenuInteraction,
9+
StringSelectMenuOptionBuilder,
1010
} from "discord.js";
1111
import { Command } from "../../interfaces/command";
1212
import {
@@ -20,10 +20,12 @@ import {
2020
createPadWithoutLimit,
2121
getMessagesOfCategories,
2222
} from "../../utils/messages";
23-
import { DiscordButtonInteraction } from "../../interfaces/interaction";
23+
import { DiscordInputInteraction } from "../../interfaces/interaction";
24+
25+
const customId = "archive-ctf-interaction";
2426

2527
async function handleArchiveInteraction(
26-
interaction: ButtonInteraction,
28+
interaction: StringSelectMenuInteraction,
2729
ctfName: string
2830
) {
2931
const guild = interaction.guild;
@@ -54,10 +56,10 @@ async function handleArchiveInteraction(
5456
return true;
5557
}
5658

57-
export const HandleArchiveCtfInteraction: DiscordButtonInteraction = {
58-
customId: "archive-ctf-button",
59-
handle: async (client: Client, interaction: ButtonInteraction) => {
60-
const ctfName = interaction.customId.replace("archive-ctf-button-", "");
59+
export const HandleArchiveCtfInteraction: DiscordInputInteraction = {
60+
customId: customId,
61+
handle: async (client: Client, interaction: StringSelectMenuInteraction) => {
62+
const ctfName = interaction.values[0];
6163
await interaction.deferUpdate();
6264
await interaction.editReply({
6365
content: `Archiving the CTF channels and roles for ${ctfName}`,
@@ -97,19 +99,23 @@ async function archiveCtfLogic(
9799
return;
98100
}
99101

100-
const buttons: ButtonBuilder[] = [];
102+
const options: StringSelectMenuOptionBuilder[] = [];
101103
for (let i = 0; i < ctfNames.length; i++) {
102-
buttons.push(
103-
new ButtonBuilder()
104-
.setCustomId(`archive-ctf-button-${ctfNames[i]}`)
104+
options.push(
105+
new StringSelectMenuOptionBuilder()
105106
.setLabel(ctfNames[i])
106-
.setStyle(ButtonStyle.Success)
107+
.setValue(ctfNames[i])
107108
);
108109
}
109110

110-
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
111-
buttons
112-
);
111+
const select = new StringSelectMenuBuilder();
112+
select
113+
.setCustomId(customId)
114+
.setPlaceholder("Choose the CTF")
115+
.addOptions(options);
116+
117+
const actionRow =
118+
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(select);
113119

114120
await interaction.editReply({
115121
content: "Which CTF do you want to archive?",

api/src/discord/agile/commands/createCtf.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {
22
ActionRowBuilder,
33
ApplicationCommandType,
4-
ButtonBuilder,
5-
ButtonInteraction,
6-
ButtonStyle,
74
Client,
85
CommandInteraction,
96
PermissionFlagsBits,
7+
StringSelectMenuBuilder,
8+
StringSelectMenuInteraction,
9+
StringSelectMenuOptionBuilder,
1010
} from "discord.js";
1111
import { Command } from "../../interfaces/command";
1212
import {
@@ -18,13 +18,15 @@ import {
1818
createChannelsAndRolesForCtf,
1919
getChannelCategoriesForCtf,
2020
} from "../channels";
21-
import { DiscordButtonInteraction } from "../../interfaces/interaction";
21+
import { DiscordInputInteraction } from "../../interfaces/interaction";
2222
import { getChallengesFromDatabase } from "../../database/tasks";
2323

24-
export const HandleCreateCtfInteraction: DiscordButtonInteraction = {
25-
customId: "create-ctf-button",
26-
handle: async (client: Client, interaction: ButtonInteraction) => {
27-
const ctfName = interaction.customId.replace("create-ctf-button-", "");
24+
const customId = "create-ctf-interaction";
25+
26+
export const HandleCreateCtfInteraction: DiscordInputInteraction = {
27+
customId: customId,
28+
handle: async (client: Client, interaction: StringSelectMenuInteraction) => {
29+
const ctfName = interaction.values[0];
2830
await interaction.deferUpdate();
2931
await interaction.editReply({
3032
content: `Creating the CTF channels and roles for ${ctfName}`,
@@ -79,21 +81,23 @@ async function createCtfLogic(client: Client, interaction: CommandInteraction) {
7981
return;
8082
}
8183

82-
// Make a loop to create buttons for each CTF
83-
const buttons: ButtonBuilder[] = [];
84+
const options: StringSelectMenuOptionBuilder[] = [];
8485
for (let i = 0; i < ctfNames.length; i++) {
85-
buttons.push(
86-
new ButtonBuilder()
87-
.setCustomId(`create-ctf-button-${ctfNames[i]}`)
86+
options.push(
87+
new StringSelectMenuOptionBuilder()
8888
.setLabel(ctfNames[i])
89-
.setStyle(ButtonStyle.Success)
89+
.setValue(ctfNames[i])
9090
);
9191
}
92+
const select = new StringSelectMenuBuilder();
93+
select
94+
.setCustomId(customId)
95+
.setPlaceholder("Choose the CTF")
96+
.addOptions(options);
9297

9398
// Create the action row with the button components
94-
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
95-
buttons
96-
);
99+
const actionRow =
100+
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(select);
97101

98102
await interaction.editReply({
99103
content: ctfNamesMessage,

api/src/discord/agile/commands/deleteCtf.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {
22
ActionRowBuilder,
33
ApplicationCommandType,
4-
ButtonBuilder,
5-
ButtonInteraction,
6-
ButtonStyle,
74
Client,
85
CommandInteraction,
96
Interaction,
107
PermissionFlagsBits,
8+
StringSelectMenuBuilder,
9+
StringSelectMenuInteraction,
10+
StringSelectMenuOptionBuilder,
1111
} from "discord.js";
1212
import { Command } from "../../interfaces/command";
1313
import {
@@ -18,7 +18,9 @@ import { getChannelCategoriesForCtf } from "../channels";
1818
import { handleDeleteCtf } from "../hooks";
1919
import { getTaskByCtfIdAndNameFromDatabase } from "../../database/tasks";
2020
import { discordArchiveTaskName } from "../../utils/messages";
21-
import { DiscordButtonInteraction } from "../../interfaces/interaction";
21+
import { DiscordInputInteraction } from "../../interfaces/interaction";
22+
23+
const customId = "delete-ctf-interaction";
2224

2325
export async function handleDeleteInteraction(
2426
interaction: Interaction,
@@ -58,19 +60,23 @@ async function deleteCtfLogic(client: Client, interaction: CommandInteraction) {
5860
return;
5961
}
6062

61-
const buttons: ButtonBuilder[] = [];
63+
const options: StringSelectMenuOptionBuilder[] = [];
6264
for (let i = 0; i < ctfNames.length; i++) {
63-
buttons.push(
64-
new ButtonBuilder()
65-
.setCustomId(`delete-ctf-button-${ctfNames[i]}`)
65+
options.push(
66+
new StringSelectMenuOptionBuilder()
6667
.setLabel(ctfNames[i])
67-
.setStyle(ButtonStyle.Success)
68+
.setValue(ctfNames[i])
6869
);
6970
}
7071

71-
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
72-
buttons
73-
);
72+
const select = new StringSelectMenuBuilder();
73+
select
74+
.setCustomId(customId)
75+
.setPlaceholder("Choose the CTF")
76+
.addOptions(options);
77+
78+
const actionRow =
79+
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(select);
7480

7581
await interaction.editReply({
7682
content:
@@ -79,10 +85,10 @@ async function deleteCtfLogic(client: Client, interaction: CommandInteraction) {
7985
});
8086
}
8187

82-
export const HandleDeleteCtfInteraction: DiscordButtonInteraction = {
83-
customId: "delete-ctf-button",
84-
handle: async (client: Client, interaction: ButtonInteraction) => {
85-
const ctfName = interaction.customId.replace("delete-ctf-button-", "");
88+
export const HandleDeleteCtfInteraction: DiscordInputInteraction = {
89+
customId: customId,
90+
handle: async (client: Client, interaction: StringSelectMenuInteraction) => {
91+
const ctfName = interaction.values[0];
8692
await interaction.deferUpdate();
8793
await interaction.editReply({
8894
content: `Deleting the CTF channels and roles for ${ctfName}`,
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { ButtonInteraction, Client } from "discord.js";
1+
import { Client, StringSelectMenuInteraction } from "discord.js";
22

3-
export interface DiscordButtonInteraction {
3+
export interface DiscordInputInteraction {
44
customId: string;
5-
handle: (client: Client, interaction: ButtonInteraction) => Promise<void>;
5+
handle: (
6+
client: Client,
7+
interaction: StringSelectMenuInteraction
8+
) => Promise<void>;
69
}

api/src/discord/listeners/interactionCreate.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ import {
77
export default (client: Client): void => {
88
client.on("interactionCreate", async (interaction: Interaction) => {
99
//check if it is a button interaction
10-
if (interaction.isButton()) {
11-
await handleButtonInteraction(client, interaction);
10+
if (interaction.isStringSelectMenu()) {
11+
await handleInputInteraction(client, interaction);
1212
} else if (interaction.isCommand() || interaction.isContextMenuCommand()) {
1313
await handleSlashCommand(client, interaction);
1414
}
1515
});
1616
};
1717

18-
const handleButtonInteraction = async (
18+
const handleInputInteraction = async (
1919
client: Client,
2020
interaction: Interaction
2121
): Promise<void> => {
22-
if (!interaction.isButton()) {
22+
if (!interaction.isStringSelectMenu()) {
2323
return;
2424
}
2525
const handler = (await getChannelHandleStyleInteractions()).find((i) =>

api/src/discord/utils/channelStyle.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import config from "../../config";
22
import { Hooks } from "../interfaces/hooks";
33
import { Command } from "../interfaces/command";
4-
import { DiscordButtonInteraction } from "../interfaces/interaction";
4+
import { DiscordInputInteraction } from "../interfaces/interaction";
55

66
const channelStyle = config.discord.channelHandleStyle;
77

88
export async function getChannelHandleStyleInteractions(): Promise<
9-
DiscordButtonInteraction[]
9+
DiscordInputInteraction[]
1010
> {
1111
switch (channelStyle) {
1212
case "agile": {

0 commit comments

Comments
 (0)