jda.wiki
Open in
urlscan Pro
185.199.110.153
Public Scan
URL:
https://jda.wiki/using-jda/interactions/
Submission: On October 05 via api from US — Scanned from IT
Submission: On October 05 via api from US — Scanned from IT
Form analysis
2 forms found in the DOM<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="purple" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette"
id="__palette_0">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31">
</path>
</svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="purple" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z">
</path>
</svg>
</label>
</form>
Name: search —
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required="">
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"></path>
</svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
</svg>
</button>
</nav>
</form>
Text Content
Skip to content JDA Wiki Interactions Type to start searching JDA * v5.1.2 * 4.3k * 737 * Getting Started * Setup * Using JDA * Contributing * Javadocs JDA Wiki JDA * v5.1.2 * 4.3k * 737 * Getting Started Getting Started * JDA * Using Events * List of Events * FAQ * Migration V3 -> V4 * Migration V4 -> V5 * Setup Setup * IntelliJ IDEA Setup * Eclipse Setup * Netbeans Setup * Logging (SLF4J) * Using JDA Using JDA * Getting Started * Making a Music Bot * Using RestAction * Interactions Interactions Table of contents * Ephemeral Messages * Command Interactions * Slash Commands * Creating Slash Commands * Responding to Slash Commands * Managing Slash Command Permissions * Slash Command Autocomplete * Handling Autocomplete * Context Menus * Component Interactions * Buttons * Handling ButtonInteractionEvent * Select Menus (Dropdowns) * String Select Menus * Entity Select Menus * Handling Select Menus * Modals * Replying with a Modal * Handling ModalInteractionEvent * Paginating Entities * Gateway Intents and Member Cache Policy * Webhooks * Managers and Moderation * Using New Features * Troubleshooting * Contributing Contributing * Contributing * Structure Guide * Repository Structure * Javadocs INTERACTIONS¶ Interactions differ between JDA v4 and JDA v5. This page covers JDA v5. This guide will give you a brief introduction to an API for adding and handling interactions in Discord. Interactions are a way to integrate your bot features directly into the Discord User Interface. These things include features such as: * Slash Commands * Slash Command Autocomplete * Context Menus * Buttons * Select Menus (Dropdowns) * Modals EPHEMERAL MESSAGES¶ Ephemeral messages are only visible to the user who used your Interaction. They are similar to the messages Discord sends you when you update your nickname with /nick. There are many limitations to ephemeral messages, a few of which are listed below: * Cannot be reacted to * Cannot be retrieved * Will be removed after a client restart Example You can only create ephemeral messages with interactions. For example with deferReply(true), reply(content).setEphemeral(true), or getHook().sendMessage(content).setEphemeral(true). For convenience, you can also configure the InteractionHook to default to ephemeral messages with hook.setEphemeral(true). COMMAND INTERACTIONS¶ SLASH COMMANDS¶ A slash command is something you might already be familiar with from the olden times of Discord. Commands such as /shrug or /me have existed for quite a long time. With Slash Command interactions you can now make your very own commands like this! But these commands come with some limitations, which I have explained in this gist: Slash Command Limitations All of these commands are used through the interactions API. They do not require the user to send an actual message in the channel and you don't have to do string parsing either. To create commands you need to make some API requests. There are 2 types of commands you can create called global commands and guild commands. * Global: These commands are available in every server your bot is in (regardless of sharding!) and direct message (Private Channels). * Guild: These commands are only in the specific guild that you created them in and cannot be used in direct messages. CREATING SLASH COMMANDS¶ You can create commands through these methods in JDA: * updateCommands() * upsertCommand(name, description) Example JavaKotlin guild.updateCommands().addCommands( Commands.slash("echo", "Repeats messages back to you.") .addOption(OptionType.STRING, "message", "The message to repeat.") .addOption(OptionType.INTEGER, "times", "The number of times to repeat the message.") .addOption(OptionType.BOOLEAN, "ephemeral", "Whether or not the message should be sent as an ephemeral message."), Commands.slash("animal", "Finds a random animal") .addOptions( new OptionData(OptionType.STRING, "type", "The type of animal to find") .addChoice("Bird", "bird") .addChoice("Big Cat", "bigcat") .addChoice("Canine", "canine") .addChoice("Fish", "fish") ) ).queue(); guild.updateCommands().addCommands( Commands.slash("echo", "Repeats messages back to you.") .addOption(OptionType.STRING, "message", "The message to repeat.") .addOption(OptionType.INTEGER, "times", "The number of times to repeat the message.") .addOption(OptionType.BOOLEAN, "ephemeral", "Whether or not the message should be sent as an ephemeral message."), Commands.slash("animal", "Finds a random animal") .addOptions( OptionData(OptionType.STRING, "type", "The type of animal to find") .addChoice("Bird", "bird") .addChoice("Big Cat", "bigcat") .addChoice("Canine", "canine") .addChoice("Fish", "fish") ) ).queue() You need to call these on a JDA instance to create global commands, and on a Guild instance to create guild commands . Your bot needs the applications.commands scope in addition to the bot scope for your bot invite link. Example: https://discord.com/oauth2/authorize?client_id=123456789&scope=bot+applications.commands Warning If the applications.commands scope is not present, JDA will fail to create guild commands, and global commands will not be shown in the client. Once a command is created, it will continue persisting even when your bot restarts. Commands stay until the bot is either kicked or your bot explicitly deletes the command. You don't need to create your commands every time your bot starts! RESPONDING TO SLASH COMMANDS¶ When a user tries to use one of your commands you will receive a SlashCommandInteractionEvent. This event needs to be handled by your event listener. The flow of a slash command response is as follows: 1. Acknowledge the command This means you need to either reply or deferReply. You only have 3 SECONDS to acknowledge a command. Since some commands may take longer than 3 seconds you may want to use deferReply to have more time for handling. This will instead send a Thinking... message to channel which is later updated by a followup message (see step 2). 2. Send followup messages Sometimes commands need more than one response. However, you can only send one initial reply to a command. To send additional messages for the same slash command you need to use the InteractionHook attached to the event with getHook(). This is a webhook that allows you to send additional messages for up to 15 minutes after the initial command. When you use deferReply the first message sent to this webhook will act identically to using editOriginal(...). The message you send is also referred to as deferred reply in this case. Your deferred reply will edit your initial Thinking... message instead of sending an additional message to channel. This means you cannot use setEphemeral on this deferred reply since you already decided whether the message will be ephemeral through your initial acknowledgment. Example Reply JavaKotlin public class SayCommand extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("say")) { event.reply(event.getOption("content").getAsString()).queue(); // reply immediately } } } object SayCommand : ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "say") { event.reply(event.getOption("content")!!.asString).queue() } } } Example Deferred Reply JavaKotlin public class TagCommand extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("tag")) { event.deferReply().queue(); // Tell discord we received the command, send a thinking... message to the user String tagName = event.getOption("name").getAsString(); TagDatabase.fingTag(tagName, (tag) -> event.getHook().sendMessage(tag).queue() // delayed response updates our inital "thinking..." message with the tag value ); } } } object TagCommand : ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "tag") { event.deferReply().queue() // Tell discord we received the command, send a thinking... message to the user val tagName = event.getOption("name")!!.asString TagDatabase.fingTag(tagName) { tag -> event.hook.sendMessage(tag).queue() // delayed response updates our inital "thinking..." message with the tag value } } } } MANAGING SLASH COMMAND PERMISSIONS¶ Restricting slash commands to specific roles or members is up to the admins of the guild. However, bots can specify a set of permissions that sets who can use those commands by default. Note that members with Permission.ADMINISTRATOR can always use commands, and they can override which roles or members can run the command. JDA only changes the default members and roles that can access it. In order to set this default, .setDefaultPermissions() can be called on the command before you register it. DefaultMemberPermissions.DISABLED can be used to only allow administrators by default, and DefaultMemberPermissions.ENABLED can be used to allow all members by default. Example This will restrict the command so that only members with the Permission.MANAGE_CHANNEL and Permission.MODERATE_MEMBERS permissions can use the command: JavaKotlin guild.updateCommands().addCommands( Commands.slash("meeting", "Creates a new channel for an emergency meeting") .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_CHANNEL, Permission.MODERATE_MEMBERS)) ).queue(); guild.updateCommands().addCommands( Commands.slash("meeting", "Creates a new channel for an emergency meeting") .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_CHANNEL, Permission.MODERATE_MEMBERS)) ).queue() SLASH COMMAND AUTOCOMPLETE¶ Slash command autocompletion is a feature that allows users to begin typing arguments to a command, and suggestions from the bot will be displayed to the user, in-client. Any slash command arguments using the String, Integer or Number types can be autocompleted. By default, options are not autocompleted. In order to make an option autocompleted, OptionData#setAutoComplete(true) may be used. HANDLING AUTOCOMPLETE¶ As the user is typing an argument that has autocomplete enabled for it, the bot will receive an CommandAutoCompleteInteractionEvent. This event isn't fired for each keystroke, but is sent when Discord determines the user has paused typing for a bit. Autocompletions can suggest up to 25 options, and users do not have to send a command with one of the options. Example Creating the command: JavaKotlin guild.updateCommands().addCommands( Commands.slash("fruit", "find a given fruit") .addOption(OptionType.STRING, "name", "fruit to find", true, true) ).queue(); guild.updateCommands().addCommands( Commands.slash("fruit", "find a given fruit") .addOption(OptionType.STRING, "name", "fruit to find", required=true, autocomplete=true) ).queue() Handling the event: JavaKotlin AutoCompleteBot.java public class AutoCompleteBot extends ListenerAdapter { private String[] words = new String[]{"apple", "apricot", "banana", "cherry", "coconut", "cranberry"}; @Override public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event) { if (event.getName().equals("fruit") && event.getFocusedOption().getName().equals("name")) { List<Command.Choice> options = Stream.of(words) .filter(word -> word.startsWith(event.getFocusedOption().getValue())) // only display words that start with the user's current input .map(word -> new Command.Choice(word, word)) // map the words to choices .collect(Collectors.toList()); event.replyChoices(options).queue(); } } } AutoCompleteBot.kt object AutoCompleteBot : ListenerAdapter() { private val words = arrayOf("apple", "apricot", "banana", "cherry", "coconut", "cranberry") override fun onCommandAutoCompleteInteraction(event: CommandAutoCompleteInteractionEvent) { if (event.name == "fruit" && event.focusedOption.name == "name") { event.replyChoiceStrings(words.filter { it.startsWith(event.focusedOption.value) }).queue() } } } CONTEXT MENUS¶ Context Menu commands are a special type of command that can be invoked on a user or message by right-clicking on them. These commands take no arguments, and are useful for providing a quick way to perform actions on a user or message. Example Creating the commands: guild.updateCommands().addCommands( Commands.context(Command.Type.USER, "Get user avatar"), Commands.message("Count words") ).queue() Handling the events: JavaKotlin ContextMenuBot.java public class ContextMenuBot extends ListenerAdapter { @Override public void onUserContextInteraction(UserContextInteractionEvent event) { if (event.getName().equals("Get user avatar")) { event.reply("Avatar: " + event.getTarget().getEffectiveAvatarUrl()).queue(); } } @Override public void onMessageContextInteraction(MessageContextInteractionEvent event) { if (event.getName().equals("Count words")) { event.reply("Words: " + event.getTarget().getContentRaw().split("\\s+").length).queue(); } } } ContextMenuBot.kt object ContextMenuBot : ListenerAdapter() { override fun onUserContextInteraction(event: UserContextInteractionEvent) { if (event.name == "Get user avatar") { event.reply("Avatar: " + event.target.effectiveAvatarUrl).queue() } } override fun onMessageContextInteraction(event: MessageContextInteractionEvent) { if (event.name == "Count words") { event.reply("Words: " + event.target.contentRaw.split(Regex("\\s+")).size).queue() } } } COMPONENT INTERACTIONS¶ To add components to a message you can use up to 5 ActionRows. You can add multiple ActionRows with either setComponents or addComponents. For the common case of a single ActionRow you can also use setActionRow(Component...) or addActionRow(Component...). Each ActionRow can hold up to a certain amount of components: * 5 Buttons * 1 Select Menu (Dropdown) * 1 Text Input (Restricted to Modals) These component interactions offer 4 response types: * Reply * Deferred Reply * Edit Message * Deferred Edit Message The reply and deferred reply responses are identical to the Slash-Commands response types. However, these new edit response types are used to update the existing message the component is attached to. If you just want to acknowledge that the component was successfully interacted with, you can simply call deferEdit() without any further updates, which will prevent the interaction from failing on the user side. To properly use an interactive component, you need to use the Component ID (aka Custom ID). This ID can also be used to then identify which component was pressed by the user. Such Component ID is provided by getComponentId() on every Component Interaction. Keep in mind that these Component IDs must be unique across all components in one message. BUTTONS¶ Each button can be enabled or disabled, have a specific style, label, and emoji: HANDLING BUTTONINTERACTIONEVENT¶ When a user presses one of these buttons, you will receive a ButtonInteractionEvent for the respective interaction. Each non-link button requires such an ID in order to be used. Example JavaKotlin public class HelloBot extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("hello")) { event.reply("Click the button to say hello") .addActionRow( Button.primary("hello", "Click Me"), // Button with only a label Button.success("emoji", Emoji.fromFormatted("<:minn:245267426227388416>"))) // Button with only an emoji .queue(); } else if (event.getName().equals("info")) { event.reply("Click the buttons for more info") .addActionRow( // link buttons don't send events, they just open a link in the browser when clicked Button.link("https://github.com/discord-jda/JDA", "GitHub") .withEmoji(Emoji.fromFormatted("<:github:849286315580719104>")), // Link Button with label and emoji Button.link("https://docs.jda.wiki/", "Javadocs")) // Link Button with only a label .queue(); } } @Override public void onButtonInteraction(ButtonInteractionEvent event) { if (event.getComponentId().equals("hello")) { event.reply("Hello :)").queue(); // send a message in the channel } else if (event.getComponentId().equals("emoji")) { event.editMessage("That button didn't say click me").queue(); // update the message } } } object HelloBot: ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "hello") { event.reply("Click the button to say hello") .addActionRow( Button.primary("hello", "Click Me"), // Button with only a label Button.success("emoji", Emoji.fromFormatted("<:minn:245267426227388416>"))) // Button with only an emoji .queue() } else if (event.name == "info") { event.reply("Click the buttons for more info") .addActionRow( // link buttons don't send events, they just open a link in the browser when clicked Button.link("https://github.com/discord-jda/JDA", "GitHub") .withEmoji(Emoji.fromFormatted("<:github:849286315580719104>")), // Link Button with label and emoji Button.link("https://docs.jda.wiki/", "Javadocs")) // Link Button with only a label .queue() } } override fun onButtonInteraction(event: ButtonInteractionEvent) { when (event.componentId) { "hello" -> event.reply("Hello :)").queue() // send a message in the channel "emoji" -> event.editMessage("That button didn't say click me").queue() // update the message } } } SELECT MENUS (DROPDOWNS)¶ Select Menus can be disabled. It's possible to set the minimum and maximum number of options to be selected. There are two implementations for SelectMenus: * StringSelectMenu supports custom string choices * EntitySelectMenu derives its choices from mentionable entities (such as Users, Channels, etc) STRING SELECT MENUS¶ String Select Menus support up to 25 options. Each option can have its own label, description, and emoji. There can be multiple options selected and set as default. ENTITY SELECT MENUS¶ Entity Select Menus do not support custom choices. Instead, they derive their choices from mentionable Discord entities such as Users, Channels, etc. You can specify which entity types you wish to appear as choices by specifying the SelectTargets in EntitySelectMenu.create. If you create an entity select menu with channel type targets, the same menu may not utilize user or role select targets, and vice versa. You can limit channel selections to specific channel types by using the setChannelTypes method on EntitySelectMenu.Builder. Info These Select Menus do not support setting a custom list of options or customizing the appearance in any way. They always show the complete list of possible entities for the specified types. HANDLING SELECT MENUS¶ When a user selects their options from a dropdown and submits their choices, you will receive either one of the following for the respective interaction: * A StringSelectInteractionEvent for a String Select Menu interaction * An EntitySelectInteractionEvent for an Entity Select Menu interaction Both interaction events provide the values that were selected by the user. Example String Select Handling JavaKotlin public class StringDropdownBot extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("food")) { event.reply("Choose your favorite food") .addActionRow( StringSelectMenu.create("choose-food") .addOption("Pizza", "pizza", "Classic") // SelectOption with only the label, value, and description .addOptions(SelectOption.of("Hamburger", "hamburger") // another way to create a SelectOption .withDescription("Tasty") // this time with a description .withEmoji(Emoji.fromUnicode("\uD83C\uDF54")) // and an emoji .withDefault(true)) // while also being the default option .build()) .queue(); } } @Override public void onStringSelectInteraction(StringSelectInteractionEvent event) { if (event.getComponentId().equals("choose-food")) { event.reply("You chose " + event.getValues().get(0)).queue(); } } } object StringDropdownBot : ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "food") { val selectMenu = StringSelectMenu.create("choose-food") .addOption("Pizza", "pizza", "Classic") // SelectOption with only the label, value, and description .addOptions(SelectOption.of("Hamburger", "hamburger") // another way to create a SelectOption .withDescription("Tasty") // this time with a description .withEmoji(Emoji.fromUnicode("\uD83C\uDF54")) // and an emoji .withDefault(true)) // while also being the default option .build() event.reply("Choose your favorite food") .addActionRow(selectMenu) .queue() } } override fun onStringSelectInteraction(event: StringSelectInteractionEvent) { if (event.componentId == "choose-food") { event.reply("You chose " + event.values[0]).queue() } } } Example Entity Select Handling JavaKotlin public class EntityDropdownBot extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("highfive")) { event.reply("Choose the user to high-five") .addActionRow( EntitySelectMenu.create("choose-user", SelectTarget.USER) .build()) .queue(); } } @Override public void onEntitySelectInteraction(EntitySelectInteractionEvent event) { if (event.getComponentId().equals("choose-user")) { // Mentions provide the selected values using familiar getters List<User> users = event.getMentions().getUsers(); event.reply("You high-fived " + users.get(0).getAsMention()).queue(); } } } object EntityDropdownBot : ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "food") { val selectMenu = EntitySelectMenu.create("choose-user", SelectTarget.USER) .build() event.reply("Choose the user to high-five") .addActionRow(selectMenu) .queue() } } override fun onEntitySelectInteraction(event: EntitySelectInteractionEvent) { if (event.componentId == "choose-user") { // Mentions provide the selected values using familiar getters val users = event.mentions.users; event.reply("You high-fived " + users.first().asMention).queue() } } } MODALS¶ Modals are pop-ups that appear in a user's Discord client. Similarly to messages, Modals can contain up to 5 ActionRows, although the only component that can be put inside Modals at the moment (TextInput) takes up a whole ActionRow. REPLYING WITH A MODAL¶ Example JavaKotlin public class SupportCommand extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { if (event.getName().equals("modmail")) { TextInput subject = TextInput.create("subject", "Subject", TextInputStyle.SHORT) .setPlaceholder("Subject of this ticket") .setMinLength(10) .setMaxLength(100) // or setRequiredRange(10, 100) .build(); TextInput body = TextInput.create("body", "Body", TextInputStyle.PARAGRAPH) .setPlaceholder("Your concerns go here") .setMinLength(30) .setMaxLength(1000) .build(); Modal modal = Modal.create("modmail", "Modmail") .addComponents(ActionRow.of(subject), ActionRow.of(body)) .build(); event.replyModal(modal).queue(); } } } object SupportCommand : ListenerAdapter() { override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) { if (event.name == "modmail") { val subject = TextInput.create("subject", "Subject", TextInputStyle.SHORT) .setPlaceholder("Subject of this ticket") .setMinLength(10) .setMaxLength(100) // or setRequiredRange(10, 100) .build() val body = TextInput.create("body", "Body", TextInputStyle.PARAGRAPH) .setPlaceholder("Your concerns go here") .setMinLength(30) .setMaxLength(1000) .build() val modal = Modal.create("modmail", "Modmail") .addComponents(ActionRow.of(subject), ActionRow.of(body)) .build() event.replyModal(modal).queue() } } } HANDLING MODALINTERACTIONEVENT¶ When the user clicks the "Submit" button on the Modal, you will receive an ModalInteractionEvent, containing all the values the user put in it. Warning Acknowledging a ModalInteractionEvent is necessary. Failing to respond to the event will not close it on the user's client, and will show them an error. Info If the Modal originated from a Component-Interaction (Buttons, SelectMenus), it is possible to acknowledge the interaction using an edit to the original Message using editMessage() or similar. Example JavaKotlin public class ModalListener extends ListenerAdapter { @Override public void onModalInteraction(@Nonnull ModalInteractionEvent event) { if (event.getModalId().equals("modmail")) { String subject = event.getValue("subject").getAsString(); String body = event.getValue("body").getAsString(); createSupportTicket(subject, body); event.reply("Thanks for your request!").setEphemeral(true).queue(); } } } object ModalListener : ListenerAdapter() { override fun onModalInteraction(event: ModalInteractionEvent) { if (event.modalId == "modmail") { val subject = event.getValue("subject") ?: return val body = event.getValue("body") ?: return createSupportTicket(subject, body) event.reply("Thanks for your request!").setEphemeral(true).queue() } } } RESOURCES * Javadocs * GitHub (JDA) * GitHub (Wiki) * Discord API Docs COMMUNITY * Discord Server * Open Collective Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and Wiki Contributors