Skip to content

Interactions

RyanLand edited this page Nov 7, 2024 · 6 revisions

This guide will show you how to create and handle interactions (excluding commands).

Table of Contents

Creating Buttons

In Colossus, buttons are created using the BaseButton class. There are several way to instantiate a button:

  • new BaseButton(button)
    Creates a simple button with no functionality. Useful for e.g. link buttons.

    • Button button - A JDA Button object. Refer to JDA documentation for more info. Example: Button.secondary("createticket", "Create Ticket")
  • new BaseButton(button, onClick)
    Creates a button with a consumer. Anyone who can see this button, can use it.

    • Button button - A JDA Button object.
    • CommandConsumer<ButtonClickEvent> onClick - The code to execute in case the button is clicked, with the click event (subclass of RepliableEvent) provided as parameter. Example: event -> event.reply("Clicked!")
  • BaseButton.predicate(predicate, ifFalse, button, onClick)
    Creates a button which can only be executed if the provided predicate returns true.

    • CommandPredicate<ButtonClickEvent> predicate - The predicate to check. Example: event -> event.getMember().isOwner()
    • CommandConsumer<ButtonClickEvent> ifFalse - The code to execute in case the button is clicked and the predicate returns false.
    • Button button - A JDA Button object.
    • CommandConsumer<ButtonClickEvent> onClick - The code to execute in case the button is clicked and the predicate returns true.
  • BaseButton.user(userId, button, onClick)
    Creates a button which can only be used by a specific user, otherwise returns an error message indicating they are not allowed to use this button.

    • Long userId - The ID of the user which may use this button
    • Button button - A JDA Button object
    • CommandConsumer<ButtonClickEvent> onClick - The code to execute in case the button is clicked and the executing user's ID matches userId.
  • BaseButton.group(userIds, button, onClick)
    Creates a button which can only be used by a specific few users, otherwise returns an error message indicating they are not allowed to use this button.

    • Long[] userIds - The IDs of the users who may use this button
    • Button button - A JDA Button object
    • CommandConsumer<ButtonClickEvent> onClick - The code to execute in case the button is clicked and the executing user's ID is inside userIds.

Hint
You can throw CommandExceptions inside of CommandPredicate and CommandConsumer, and Colossus will return an error embed to the user.

Listening to Buttons

There are several ways to listen to buttons. The easiest way is to use the PresetBuilder, which will register listeners automatically.

Listening to buttons using PresetBuilder

You can add buttons to a PresetBuilder using the PresetBuilder#addButtons(BaseButton...) method.

A call to this method will create a ButtonRow, which is a row of buttons. A row of buttons can contain up to 5 buttons. If you provide more than 5 buttons, multiple rows of buttons will be created.

Once you reply to a RepliableEvent using a PresetBuilder, listeners for the buttons will be automatically registered. If, for some reason, you do not have access to a RepliableEvent object, you can use the PresetBuilder#addComponentRowListeners method to register the listeners manually.

Example:

BaseButton button = new BaseButton(
    Button.primary("test", "Test"), // create a button with ID "test" and label "Test"
    clickEvent -> clickEvent.reply("Clicked!", true) // reply "Clicked!" in an ephemeral message to users who click the button
);
PresetBuilder pb = new PresetBuilder()
    .setTitle("Buttons")
    .setDescription("Click the button below!")
    .addButtons(button); // add the button to a PresetBuilder
event.reply(pb); // reply to the RepliableEvent with this PresetBuilder

Result:
image

Please note: Buttons registered this way will stop working after a bot restart. They will also be destroyed after 24 hours by default. This time period can be modified using ColossusBuilder#setDefaultComponentListenerExpirationTime.

If you want to listen to buttons permanently, please keep reading:

Listening to buttons using @ButtonListener

There are two key differences between this approach and the approach described above:

  1. These listeners will be registered permanently, even after a bot restart.
  2. These listeners are not bound to a specific instance of a message, the other approach is. This means that any button with the provided ID will execute the given code.

To start listening, you need to create a static method annotated with @ButtonListener and precisely one parameter of type ButtonClickEvent. This method may live in any class.
The method body will be executed immediately after a button with the provided ID is clicked.

The listener will be automatically registered using ColossusBuilder#scanPackage.

Example:

@Override
public void run(SlashCommandEvent event) throws CommandException {
  PresetBuilder pb = new PresetBuilder()
          .setTitle("Manage Ticket")
          .setDescription("Use the buttons below to manage your ticket.")
          .addButtons(Button.danger("close", "Close Ticket")); // add the button to a PresetBuilder
  event.reply(pb);
}

@ButtonListener("close")
private static void onCloseButtonClick(ButtonClickEvent event) {
    event.reply("Ticket closed", true);
}

Result:
image

Instead of checking if the button ID exactly matches, you may also choose to check if the button ID starts with a certain string. This functionality can be enabled using the startsWith parameter inside the @ButtonListener annotation.

This feature may be useful for storing data like IDs, primary/foreign keys, etc. inside button IDs.

Example:

@Override
public void run(SlashCommandEvent event) throws CommandException {
    PresetBuilder pb = new PresetBuilder()
        .setTitle("Manage Ticket")
        .setDescription("Use the buttons below to manage your ticket.")
        .addButtons(Button.danger("close-"+event.getUser().getId(), "Close Ticket"));
    event.reply(pb);
}

@ButtonListener(value = "close-", startsWith = true)
private static void onCloseButtonClick(ButtonClickEvent event) {
    String userId = event.getButtonId().split("-")[1];
    event.reply("Ticket closed for user <@"+userId+">", true);
}

Result:
image

Creating Select Menus

Select Menus can be created using the BaseSelectMenu class. It works in a very similar way to buttons.

  • new BaseSelectMenu(selectMenu, onSubmit)
    Creates a select menu with a consumer. Anyone who can see this select menu, can use it.

    • SelectMenu selectMenu - A JDA SelectMenu object. Refer to JDA documentation for more info. Example: StringSelectMenu.create("food").addOption("Pizza", "pizza").build()
    • CommandConsumer<SelectMenuEvent> onSubmit - The code to execute every time the select menu is used, with the submit event (subclass of RepliableEvent) provided as parameter. Example: event -> event.reply("Selected "+event.getValues().size()+" options!")
  • BaseSelectMenu.predicate(predicate, ifFalse, selectMenu, onSubmit)
    Creates a select menu which can only be executed if the provided predicate returns true.

    • CommandPredicate<SelectMenuEvent> predicate - The predicate to check. Example: event -> event.getMember().isOwner()
    • CommandConsumer<SelectMenuEvent> ifFalse - The code to execute in case the select menu is used and the predicate returns false.
    • SelectMenu selectMenu - A JDA SelectMenu object.
    • CommandConsumer<SelectMenuEvent> onSubmit - The code to execute every time the select menu is used and the predicate returns true.
  • BaseSelectMenu.user(userId, selectMenu, onSubmit)
    Creates a select menu which can only be used by a specific user, otherwise returns an error message indicating they are not allowed to use this select menu.

    • Long userId - The ID of the user which may use this select menu
    • SelectMenu selectMenu - A JDA SelectMenu object
    • CommandConsumer<SelectMenuEvent> onSubmit - The code to execute in case the select menu is used and the executing user's ID matches userId.
  • BaseSelectMenu.group(userIds, selectMenu, onSubmit)
    Creates a select menu which can only be used by a specific few users, otherwise returns an error message indicating they are not allowed to use this select menu.

    • Long[] userIds - The IDs of the users who may use this button
    • SelectMenu selectMenu - A JDA SelectMenu object
    • CommandConsumer<SelectMenuEvent> onSubmit - The code to execute in case the select menu is used and the executing user's ID is inside userIds.

Hint
You can throw CommandExceptions inside of CommandPredicate and CommandConsumer, and Colossus will return an error embed to the user.

Listening to Select Menus

Listening to select menus is similar to listening to buttons.

Listening to select menus using PresetBuilder

You can add select menus to a PresetBuilder using the PresetBuilder#addComponentRow(ComponentRow) method.

Once you reply to a RepliableEvent using a PresetBuilder, listeners for the select menus will be automatically registered. If, for some reason, you do not have access to a RepliableEvent object, you can use the PresetBuilder#addComponentRowListeners method to register the listeners manually.

Example:

BaseSelectMenu selectMenu = new BaseSelectMenu(
    StringSelectMenu.create("food")
        .addOption("Pizza", "pizza", Emoji.fromUnicode("πŸ•"))
        .addOption("Burger", "burger", Emoji.fromUnicode("πŸ”"))
        .addOption("Pasta", "pasta", Emoji.fromUnicode("🍝"))
        .addOption("Salad", "salad", Emoji.fromUnicode("πŸ₯—"))
        .build(),
    submitEvent -> submitEvent.reply("You selected "+submitEvent.getValue(), true)
);
PresetBuilder pb = new PresetBuilder()
    .setTitle("Select Food")
    .setDescription("Please select the food that you would like to order below.")
    .addComponentRow(selectMenu);
event.reply(pb);

Result:
image

Please note: Select Menus registered this way will stop working after a bot restart. They will also be destroyed after 24 hours by default. This time period can be modified using ColossusBuilder#setDefaultComponentListenerExpirationTime.

If you want to listen to select menus permanently, please keep reading:

Listening to select menus using @SelectMenuListener

There are two key differences between this approach and the approach described above:

  1. These listeners will be registered permanently, even after a bot restart.
  2. These listeners are not bound to a specific instance of a message, the other approach is. This means that any select menu with the provided ID will execute the given code.

To start listening, you need to create a static method annotated with @SelectMenuListener and precisely one parameter of type SelectMenuEvent. This method may live in any class.
The method body will be executed immediately after a select menu with the provided ID is used.

The listener will be automatically registered using ColossusBuilder#scanPackage.

Example:

@Override
public void run(SlashCommandEvent event) throws CommandException {
    BaseSelectMenu selectMenu = new BaseSelectMenu(
        StringSelectMenu.create("food")
            .addOption("Pizza", "pizza", Emoji.fromUnicode("πŸ•"))
            .addOption("Burger", "burger", Emoji.fromUnicode("πŸ”"))
            .addOption("Pasta", "pasta", Emoji.fromUnicode("🍝"))
            .addOption("Salad", "salad", Emoji.fromUnicode("πŸ₯—"))
            .build()
    );
    PresetBuilder pb = new PresetBuilder()
        .setTitle("Select Food")
        .setDescription("Please select the food that you would like to order below.")
        .addComponentRow(selectMenu);
    event.reply(pb);
}

@SelectMenuListener("food")
private static void onFoodPicked(SelectMenuEvent event) {
    event.reply("You selected "+event.getValue(), true);
}

Result:
image

Instead of checking if the select menu ID exactly matches, you may also choose to check if the select menu ID starts with a certain string. This functionality can be enabled using the startsWith parameter inside the @SelectMenuListener annotation.

This feature may be useful for storing data like IDs, primary/foreign keys, etc. inside select menu IDs.

Using Modals

You can show a modal to a user using the RepliableEvent#reply(Modal, CommandConsumer<ModalSubmitEvent>) method.
The modal will be displayed to the user, and the code inside the CommandConsumer will be executed when the user presses the "Submit" button.

Example:

event.reply(
    Modal.create("entername", "Enter Name")
        .addActionRow(
            TextInput.create("name", "Name", TextInputStyle.SHORT)
                .setPlaceholder("Enter your name")
                .setMaxLength(50)
                .build()
        )
        .build(),
    submitEvent -> {
        event.reply("You entered: "+submitEvent.getValue("name").getAsString(), true);
    }
);

Result:
DiscordPTB_o4g9aqYYhc

Using InteractionMenus

Colossus provides a way to create and manage menus, which are a combination of embeds, buttons, select menus, and modals.

To send a menu, simply use RepliableEvent#reply(InteractionMenu) - event.reply(menu).

We also provide a few pre-made menus, which can be used to create a rich menu with a single line of code.

Pre-made Menus

ConfirmMenu

Presents the user with an option to either go through with an action or cancel it. Create one using:

  • new ConfirmMenu(description, confirmedDescription, ephemeral, confirmAction)
    • String description - The description of the embed to confirm the action
    • String confirmedDescription - The description of the embed after the user has clicked the Confirm button
    • boolean ephemeral - Whether this menu should be sent as an ephemeral message
    • CommandConsumer<ButtonClickEvent> confirmAction - Code to perform when the user has clicked the Confirm button

Preview:
DiscordPTB_llgQgkW5pY

TabMenu

An advanced menu to browse custom defined pages and (nested) subpages using tabs. Create one using TabMenuBuilder.

Try it out by using the default help command. You can find the code for the command here.

SelectRowMenu

A menu to switch between different pages using a select menu. This can be useful for e.g. a settings command.

To create one, you will need to get a list of SelectRowOption first. These represent all the pages in the select menu.

SelectRowOption cardOption = new SelectRowOption(
    "Credit Card", "Edit card details.", Emoji.fromUnicode("πŸ’³"), selectEvent -> {
        BotUser user = selectEvent.getUserEntity();
        return new PresetBuilder().setDescription("Current card number: "+user.getCardNumber());
    }
);
// Please note: There are simpler constructors available for SelectRowOption. Refer to the class for more info.

Then, create a SelectRowMenu using the SelectRowMenuBuilder.

SelectRowMenu menu = new SelectRowMenuBuilder()
    .setStartMessage(new PresetBuilder("Settings", "Modify user settings."))
    .setPlaceholder("Pick a setting...")
    .addOptions(cardOption)
    .build();
menu.send(event);

Preview:
DiscordPTB_FRHc8JP7A3

ScrollPageMenu

A menu to scroll through different pages using buttons. To create one, create a list of PresetBuilder, representing the pages.

Optionally, you can provide a page number to start on. By default, this will be the first page.

event.reply(new ScrollPageMenu(pages));

Preview of a simple ScrollPageMenu:
DiscordPTB_pp9Le9BbBy

Creating your own InteractionMenu

You can create your own menu by implementing the InteractionMenu interface (and optionally InteractionMenuBuilder as well).

Feel free to take a look at the code of the pre-made menus to get an idea of how to create your own and to get inspiration.