{"id":25405037,"url":"https://github.com/mineking9534/discordtoolkit","last_synced_at":"2026-05-15T22:30:56.090Z","repository":{"id":277207414,"uuid":"931653915","full_name":"MineKing9534/DiscordToolKit","owner":"MineKing9534","description":"Extension library for JDA simplifying the process of creating commands and menus in kotlin","archived":false,"fork":false,"pushed_at":"2026-04-21T20:12:30.000Z","size":1595,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-21T21:42:28.192Z","etag":null,"topics":["commands","discord","jda","kotlin","menu","utility"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MineKing9534.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-12T16:31:13.000Z","updated_at":"2026-04-21T20:02:48.000Z","dependencies_parsed_at":"2025-02-12T18:37:11.985Z","dependency_job_id":"eae9b0bf-65c5-40d8-9dc2-c6effb8b368e","html_url":"https://github.com/MineKing9534/DiscordToolKit","commit_stats":null,"previous_names":["mineking9534/discordtoolkit"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/MineKing9534/DiscordToolKit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordToolKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordToolKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordToolKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordToolKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MineKing9534","download_url":"https://codeload.github.com/MineKing9534/DiscordToolKit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordToolKit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33081967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T20:25:35.270Z","status":"ssl_error","status_checked_at":"2026-05-15T20:25:34.732Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["commands","discord","jda","kotlin","menu","utility"],"created_at":"2025-02-16T04:29:56.282Z","updated_at":"2026-05-15T22:30:56.083Z","avatar_url":"https://github.com/MineKing9534.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![[Kotlin CI]](https://github.com/MineKing9534/DiscordToolKit/actions/workflows/beta.yml/badge.svg)\n![[Latest Version]](https://maven.mineking.dev/api/badge/latest/releases/de/mineking/DiscordToolKit?prefix=v\u0026name=Latest%20Version\u0026color=0374b5)\n![[Components V2]](https://img.shields.io/badge/Components_v2-supported-green)\n\n# Introduction\nDiscord Tool Kit (DTK) is a library built around [JDA](https://github.com/discord-jda/JDA) that aims to simplify creating and handling application commands and user interfaces with message components or modals.\nIt also provides a built-in way to localize both commands and menus.\n\nTo create a DTK instance, you can use the `discordToolKit` function. As parameters, you have to pass your `JDA` instance and optionally an instance of the main class of your bot.\nThis is useful if you already have a reference to the DTK instance available but also need access to your main class instance.\nThis is common in custom components or command implementations because they always provide a reference to their respective managers (Both CommandManager and UIManager have a reference to their DTK instance in the `manager` property).\nIf you don't provide an instance of your main class, `Unit` is used implicitly.\n\n\u003e [!TIP]\n\u003e This library is NOT compatible with pure Java. If you want to use Java instead of Kotlin, you can take a look at [DiscordUtils](https://github.com/MineKing9534/DiscordUtils) which has a similar scope as this project but was created in and for Java.\n\nThere are some examples available in `examples/src/main/kotlin/` if you want to see some features in action. If you want to run them you have to add the `-DTOKEN=\u003cYOUR BOT TOKEN\u003e` flag.\n\n\u003e [!NOTE]\n\u003e DiscordToolKit uses kotlin coroutines. Therefore, some public methods are marked as `suspend` and therefore have to be called from a coroutine context. If you don't use coroutines in your project, you can use the `runBlocking` function to call these methods.\n\n# Table of Contents\n\u003c!-- TOC --\u003e\n* [Introduction](#introduction)\n* [Table of Contents](#table-of-contents)\n* [Installation](#installation)\n* [Commands](#commands)\n  * [Execution conditions](#execution-conditions)\n  * [Options](#options)\n    * [Packed and unpacked options](#packed-and-unpacked-options)\n    * [Required and nullable options](#required-and-nullable-options)\n    * [Option Mappers](#option-mappers)\n    * [Configuring Options](#configuring-options)\n    * [Choices and Autocomplete](#choices-and-autocomplete)\n* [UI](#ui)\n  * [States](#states)\n  * [Menu Parameters](#menu-parameters)\n  * [Defer Mode](#defer-mode)\n  * [Message Menus](#message-menus)\n    * [Components](#components)\n      * [Buttons](#buttons)\n      * [Selects](#selects)\n    * [Components V2](#components-v2)\n    * [Submenus and Menu components](#submenus-and-menu-components)\n    * [Pagination](#pagination)\n    * [Text Builders](#text-builders)\n  * [Modal Menus](#modal-menus)\n  * [MenuCommand](#menucommand)\n* [Localization](#localization)\n  * [General](#general)\n  * [Command Localization](#command-localization)\n  * [Menu Localization](#menu-localization)\n\u003c!-- TOC --\u003e\n\n# Installation\nDiscordToolKit can be downloaded from a custom maven repository. To use it, add this to your `build.gradle.kts` file:\n```\nrepositories {\n//...\n    maven(\"https://maven.mineking.dev/releases\")\n//  maven(\"https://maven.mineking.dev/snapshots\") \n}\n\ndependencies {\n//...\n    implementation(\"de.mineking:DiscordToolKit:VERSION\")\n}\n```\nIf you are using Maven you can just use the maven equivalent of the above.\n\n# Commands\nTo start using the CommandManager, you first have to register it to your DTK instance. You can then start configuring it inside the configuration block:\n```kt\ndiscordToolKit(jda).withCommandManager {\n    //...\n}.build()\n```\n\nYou can register commands with either the `registerCommand` method, or simply use the `+` operator. To update your commands in discord, you can build an update request with the `updateCommands` method.\n\u003e [!NOTE]\n\u003e `updateCommands` returns a `RestAction` and does NOT automatically execute it. You still have to call `queue` on it like with other actions in JDA. \n\n```kt\ndiscordToolKit(jda).withCommandManager {\n    registerCommand(slashCommand(\"a\") {\n        //...\n    })\n    \n    +slashCommand(\"b\") {\n        //...\n    }\n\n    updateCommands().queue()\n}\n```\nYou can create commands with `slashCommand` (For normal slash commands), `userCommand` (For user context commands) and `messageCommand` (For message context commands). You can define the name (and for slash commands the description) as parameters of these functions.\nAll further configuration can be done inside the configuration block. You can define default member permissions, integration type and interaction context types there. \nDefaults for integration type and interaction context types can be specified in the command manager config block.\n\nYou can also add subcommands to slash commands. This can be done by using the `+` operator inside the slash command config block with the command to use:\n```kt\nslashCommand(\"parent\") {\n    +slashCommand(\"subcommand_a\") {\n        //...\n    }\n  \n    +slashCommand(\"subcommand_b\") {\n        //...\n    }\n}\n```\nYou can also add multiple layers of subcommands. This will result in the creation of subcommand groups. However, you are still limited by the limitations of discord, meaning a command with subcommands cannot be executed.\n\nYou can specify the command body in an `execute` block. You can also define code that should be executed before the main executors. This can be used to define preconditions:\n```kt\nslashCommand(\"test\") {\n    before {\n        //You can use terminateCommand to end command execution\n        if (user.idLong != 0) terminateCommand()\n    }\n  \n    execute {\n        reply(\"Test\").setEphemeral(true).queue()\n    }\n}\n```\nYou can also specify whether before handlers should be inherited to subcommands (only relevant for slash commands because context commands cannot have children). \nSubcommands can also specifically ignore parent conditions by calling `ignoreParentConditions` in their config block.\n\n## Execution conditions\nIf you want to use before handlers for conditions like in the example above, you can also use the `require` function. It can be used like this:\n```kt\nslashCommand(\"test\") {\n    require({ user.id == 0 }, orElse = { reply(\"You are not allowed to use this command\").setEphemeral(true).queue() })\n  \n    execute {\n        //Only executed if the condition above is met. Otherwise, the orElse handler is executed\n    }\n}\n```\n\n## Options\nSlash commands can also have options. You can define them with the `option` function in the config of the respective slash command:\n```kt\nslashCommand(\"test\") {\n    val option = option\u003cInt\u003e(\"option\", description = \"Description\")\n  \n    execute {\n        reply(\"Response: ${option().orElse(0)}\").setEphemeral(true).queue()\n    }\n}\n```\n\nInside `execute` and `before` blocks, you can access an option's value by executing it as a function as seen above. The returned object contains the actual value of the option and whether it was provided or not (useful for non-required options).\nYou can use `orElse` to get the value of the option or the provided alternative if the option was not passed. Alternatively, you could also use `get` to directly get the provided value. However, this will throw an exception if the option was not provided and should therefore only be used for required options.\n\n### Packed and unpacked options\nYou can also use `get` and `orElse` directly on the value returned by `option`:\n```kt\nslashCommand(\"test\") {\n    val option = option\u003cInt\u003e(\"option\", description = \"Description\").orElse(0)\n  \n    execute {\n        reply(\"Response: ${option()}\").setEphemeral(true).queue()\n    }\n}\n```\nThis might be more readable because the default value is defined directly next to the option declaration. You can also use `map`, `filter`, `orNull` and `or`. These are available both before and after getting the value (as seen above with `orElse`).\n\n```kt\nslashCommand(\"test\") {\n    val a = option\u003cInt\u003e(\"a\").orElse(0).map { it + 1 }\n    val b = option\u003cInt\u003e(\"b\").filterValue { it != 0 }.orElse { a() } //orElse can also be used with a functional parameter providing access to the current SlashCommandContext. This allows you to make the default value dependent on e.g. the executor\n    val c = option\u003cInt\u003e(\"c\").orValue(a) //Use orValue here because the target option is unpacked already with orElse\n  \n    val d = option\u003cInt\u003e(\"d\").mapValue { it + 1 } //Use mapValue because the contained value is still optional\n    val e = option\u003cInt\u003e(\"e\").or(d) //Use or directly because the target is already still optional\n  \n    execute {\n        //These options return the raw type because they are mapped with unpacking options above\n        val aValue: Int = a()\n        val bValue: Int = b()\n        val cValue: Int = c()\n    \n        //These options return OptionalOptions because they are only mapped without unpacking. You can still chain unpacking mappings above if you wanted to \n        val dValue: OptionalOption\u003cInt\u003e = d()\n        val eValue: OptionalOption\u003cInt\u003e = e()\n    }\n}\n```\nThe above example doesn't really make sense from a practical standpoint, but it demonstrates the different possibilities to handle options.\n\n### Required and nullable options\nOptions are optional by default. You can either make them required by passing `required = true` as parameter or by using the overloads prefixed with required, e.g. `requiredOption\u003cT\u003e`. These will also directly return an unpacked option using `get`.\nSimilarly, there is also `nullableOption\u003cT\u003e` which will unpack the option using `orNull`, meaning the value will be null if no value was provided.\n\n### Option Mappers\nAll option types are implemented using option mappers. These are used to specify how kotlin types should be used as discord functions and how to parse values from discord into kotlin objects. \nFor all default types supported by discord, option mappers are already implemented and registered automatically. \nYou can also define custom type mappers and add them to the `optionMappers` list in the command manager config block.\n\nThis would allow you to e.g. implement color options more easily:\n```kt\noptionMappers += nullSafeOptionMapper\u003cColor, String\u003e(OptionMappers.STRING, { _, _, _, _ -\u003e setRequiredLength(7, 7) }) { value, type -\u003e\n    try {\n        Color.decode(value)\n    } catch (_: NumberFormatException) {\n        //Required because this might also be executed from autocomplete\n        if (this !is SlashCommandContext) return@nullSafeOptionMapper Color.BLACK\n    \n        reply(\"Invalid Color\").setEphemeral(true).queue()\n        terminateCommand()\n    }\n}\n\n+slashCommand(\"test\") {\n    val option = option\u003cColor\u003e(\"option\").orElse(Color.BLACK)\n  \n    execute {\n        replyEmbeds(EmbedBuilder()\n            .setColor(option())\n            .setDescription(\"Test\")\n            .build()\n        ).queue()\n    }\n}\n```\n\n### Configuring Options\nFor some default options types there are functions available for easier configuration:\n```kt\nslashCommand(\"test\") {\n  val a = option\u003cInt\u003e(\"a\", configurator = { setMinValue(0); setMaxValue(10) })\n  val b = intOption(\"b\", min = 0, max = 10) //Shorter alternative\n}\n```\nThese are also available for other numeric types (`longOption`, `doubleOption`), string (for minLength and maxLength) and channel (for channel types).\nAll of these functions also have `required` and `nullable` variants available. \nIf you have a type of option that you need multiple times, you can also define your own default option types by creating extension functions for `OptionConfig`.\n\n### Choices and Autocomplete\nYou can pass a fixed set of choices to an option. A user has to select one of these choices (only possible for numeric types and strings):\n```kt\nslashCommand(\"test\") {\n    val option = option\u003cString\u003e(\"option\", choices = listOf(\n        choice(\"value\", label = \"label\")\n    ))\n}\n```\n\nIf you want to use autocomplete, you can simply pass a function as last argument. This will automatically mark the option as autocomplete and use the provided function as autocomplete handler:\n```kt\nslashCommand(\"test\") {\n    val option = option\u003cString\u003e(\"text\") { replyChoices(\n        autocompleteChoice(currentValue!!.uppercase())\n    ) }\n}\n```\n\n# UI\nThe UIManager allows to easily create both message and modal menus with the different available component types. \nThese menus can store state values that are stored inside the component ids and are therefore persisted even when restarting your bot without requiring any additional database. \nTo start using it, you have to register the UIManager to your DTK instance:\n```kt\ndiscordToolKit(jda).withUIManager {\n    //...\n}\n```\n\nMenus have to be registered once and can then be used afterward:\n```kt\ndiscordToolKit(jda).withUIManager {\n    val menu = registerMenu\u003cUnit\u003e(\"test\") {\n        //...\n    }\n    \n    jda.getTextChannelById(0).sendMenu(menu).queue()\n    //or\n    jda.getTextChannelById(0).sendMenu(getMessageMenu(\"test\")).queue()\n}\n```\n\nAt any point, you can use `terminateRender` to terminate the current render process. It can be used if you don't have any other way to manage complex render flows.\n\n\u003e [!NOTE]\n\u003e A lot of the methods you use to configure your menus are implemented in extension functions. Make sure to import these to avoid running into issues!\n\u003e For example, the `lazy` function also exists as a kotlin default function. However, this functions behaves different from the discord tool kit one (which is only available in menus).\n\n\u003e [!NOTE]\n\u003e The following sections will contain information about a few default provided components, either directly mapping components supported by discord (buttons and selects) or more complex components that consist of multiple primitive components with predefined behavior.\n\u003e These are provided by default to make it easier to implement certain functionality, however, it would theoretically be possible to implement all of these yourself. \n\u003e Therefore, if you don't like the behavior of a predefined component, you can look up its implementation and modify it to your needs.\n\n## States\nStates allow you to store information in a menu that is persistent over different renders and even bot restarts. \nThis is achieved by using the custom id space of your components to store state values (encoded in a very basic binary format using kotlinx.serialization).\n\n\u003e [!TIP]\n\u003e Discord allows bots to store up to 100 characters per component. DiscordToolKit will use this space to identify the component and the menu it belomgs to. The remaining free characters are used to store the encoded state. You should therefore try to keep your component and menu names rather short to leave more free space for states, if your menu needs as much space as possible. if you just need to store a few integers, that usually isn't a problem. \n\nIn Message menus, the menu is automatically rerendered when state values change (after all handlers are executed). If you didn't change any state values but still want to rerender, you can call `forceUpdate`.\n\nTo declare a new state, you can simply use the `state` function. There are multiple ways to use the returned value:\n- Delegation (Recommended)\n```kt\nvar state by state(0)\n\n//Read state value (Most commonly for component labels or content rendering)\nprintln(state)\n\n//Change state value (Most commonly inside component handlers)\nstate = 5\n```\n- Destructuring (Useful if you need the state reference to pass as a parameter to an advanced component)\n```kt\nval (state, setState, ref) = state(0)\n\n//Read state value\nprintln(state())\n\n//Change state value\nsetState(5)\n```\n- Raw\n```kt\nval state = state(0)\n\n//Read state value\nprintln(state.value)\n\n//Change state value\nstate.value = 5\n```\n\nTo pass a state reference to the counter component you could do\n```kt\nval (count, _, ref) = state(0)\n\nmessage { content(\"**Current Count:** ${count()}\") }\n+counter(\"counter\", ref = ref)\n```\n\n![](assets/counter.gif)\n\nYour state can be of any type, as long as it can be serialized and deserialized with Cbor. This means, that you can use custom types, as long as you define the serializers for that (See kotlin serialization documentation).\nIf you don't want to provide a default value, you can also use the following syntax:\n```kt\nval state by state\u003cInt\u003e()\n```\nThis will make your state implicitly use `null` as default value and make the type of the returned state nullable as well.\n\nYou can also define state update handlers when declaring a state value. This is especially useful when passing a reference of your state to a component and for example want to update something in a database when the state value changes.\nTo do so you can simply pass a function as last parameter:\n```kt\nval state by state(0) { old, new -\u003e pintln(\"Old: $old, New: $new\") }\n```\n\u003e [!NOTE]\n\u003e This update function is always called when a state update is triggered, regardless of whether the value actually changed.\n\u003e This means that setting the state value to the same value it had before will still cause your update handler to run.\n\u003e If you don't want this behavior, you can simply add an if-clause that checks whether the old and new values are equal\n\nYou can use state transformations to store the state in a different format than when accessing it. For example if you want to store a color as int:\n```kt\nvar color by state(0).transform({ Color(it) }, { it.rgb })\n\n//...\ncolor = Color.WHITE\n\n//...\nprintln(color.rgb)\n```\n\n## Menu Parameters\nYou might have noticed the `Unit` type parameter that is passed to registerMenu in the examples before. \nThis type is the menu parameter type. In many cases you can just use Unit because you don't need a parameter, but you might want to pass certain information when first sending a menu, like an initial state value.\n\n```kt\nval menu = registerMenu\u003cInt\u003e(\"test\") {\n    var count by state(0)\n\n    //The initialize block is only called when the menu is first sent and gives you access to the menu parameter\n    initilaize { count = it }\n    \n    message {\n      content(\"Count: $count\")   \n    }\n    \n    +actionRow(button(\"inc\", label = \"+\") { count++ })\n}\n\n//Send two menu instances, one with parameter 5 and one with parameter 10\njda.getTextChannelById(0).sendMenu(menu, 5).queue()\njda.getTextChannelById(0).sendMenu(menu, 10).queue()\n```\n\n## Defer Mode\nMenu defer modes specify whether to automatically defer menu interactions. By default, all interactions are acknowledged directly after receiving the interaction (`DeferMode.ALWAYS`).\nWhen an interaction is acknowledged, all components on the message are disabled until the menu is rerendered. This prevents \"Application did not respond\" problems when your rendering is taking too long.\n\nHowever, this does not work when trying to reply with a modal in a component handler, because discord does not allow sending modals after an interaction was acknowledged.\nIf you need to do that, you can either change the defer mode to `DeferMode.NEVER` (Will never automatically defer interactions) or `DeferMode.UNLESS_PREVENTED` (Will defer after component handlers if you did not explicitly prevent the menu from rerendering with `preventUpdate`).\n\n## Message Menus\nInside the menu configuration block you can register states, define the message content and components for the menu:\n```kt\nregisterMenu\u003cUnit\u003e(\"test\") {\n    var count by state(0)\n    \n    message {\n      content(\"Count: $count\")\n    }\n    \n    +actionRow(button(\"inc\", label = \"+\") { count++ })\n}\n```\nFor more detailed information about states see [States](#states).\nYou can define the message content, embed and attachments with the corresponding functions. \n\nTo define the message content, you can use the `message` block. Inside that block, you can define the content, attachments and embeds.\nHowever, it is recommended to use components v2 instead (See [Components V2](#components-v2)).\n\nYou can also provide a custom `MessageEditBuilder` instance to configure more advanced data for the finalized message.\nWhile components will be added to this custom builder, this action will override other message fields like content. If you want to create a custom `MessageEditBuilder` and still use these fields, just set them in the provided `MEssageEditBuilder` accordingly.\n\nFor example, you can use this to prevent mentioning users:\n```kt\nmessage(MessageEditBuilder().setAllowedMentions(emptyList()))\n```\n\nIf you have resource heavy rendering, for example, generating an image, you should write this code inside a `render` block. \nWhile the entire configuration block is executed several times, everything inside of `render` is only executed when the menu is currently rendering the message content:\n\n```kt\nregisterMenu\u003cUnit\u003e(\"menu\") {\n    var state by state(0)\n    \n    render {\n        //This code is only executed when the resulting message is actually used\n        \n        val image = generateImage(state) //Expensive render operation\n        \n        message {\n            attachment(image)\n        }\n    }\n    \n    //...\n}\n```\n\n### Components\nComponents can be added with the `+` operator. There are some components available by default, but you can also write your own components, either from scratch with default configuration for default components or by composing multiple components to one.\nThe first parameter to every default component function is its internal name. It has to be unique per menu and is used to match a component to its handler.\n\n\u003e [!NOTE]\n\u003e While all cosmetic properties of components like label etc. are allowed to change during different renders (e.g., by making it dependent on state values), the names and added components should stay the same during every call of the function.\n\u003e If you don't want to show certain components under certain conditions, you can use the `hiddenIf` function. This way the component is still added on each render but only actually shown on the message when you want to.\n\n```kt\nregisterMenu\u003cUnit\u003e(\"test\") {\n    var count by state(0)\n    \n    content(\"**Current Count:** $count\")\n    \n  +actionRow(\n    button(\"inc\", label = \"+\", color = ButtonColor.BLUE) { count++ },\n    button(\"dec\", label = \"-\", color = ButtonColor.BLUE) { count-- },\n\n    //Only show reset button if the state value is not already 0\n    button(\"rst\", label = \"Reset\", color = ButtonColor.RED) { count = 0 }.hiddenIf(count == 0)   \n  )\n}\n```\n\n![](assets/manual_counter.gif)\n\nInside the handlers you can also access the interaction event:\n```kt\nbutton(\"btn\", label = \"Button\") {\n    println(event.user.name)\n    println(user.name) //Same but shorter\n}\n```\n\nThe handler context also allows you to:\n- Switch to another menu (You should prefer using switch menu components explained in [Submenus and Menu Components](#submenus-and-menu-components))\n- Forcing the menu to rerender (Will only happen automatically if any state values change)\n- Prevent the menu from rerendering (If you don't want to rerender even if some state values changed)\n- Anything else you can do with the interaction event, like replying a new message\n\n#### Buttons\nYou can create buttons with the `button` function. You can pass a label, emoji and color, next to the general name and handler. \nYou can also use `link` to create a link button (Cannot have a handler or custom color).\n\nButtons can not directly be added to a menu. They always have to be wrapped in an `actionRow`. Discord allows up to five buttons in a single action row.\nYou can call `actionRow` with more than five buttons. However, if more than 5 buttons are rendered in an action row, an exception is thrown.\nThis means, that if you create an action row with more than five buttons, you have to ensure that at most five buttons are actually visible at a time using the `hide` or `show` methods.\n\nThere are also default implementations for `toggleButton` (toggles boolean state) and `enumToggleButton` (cycles through enum constants) available:\n```kt\nenum class Enum { A, B, C }\n\nval (value, _, ref) = state(Enum.A)\n\n+actionRow(enumToggleButton(\"button\", label = \"${value()}\", ref = ref))\n```\n\n#### Selects\nYou can create select menus with `stringSelect` or `entitySelect`. For string selects you also have to provide a list of `SelectOption`s (unless the menu is hidden or disabled).\nA `SelectOption` can be created with the `selectOption` function. You can provide a value, label, emoji, description and whether the select option is selected by default. \nAdditionally, you can also provide a handler that is called when the select option is selected:\n\n```kt\nstringSelect(\"select\", min = 1, max = 2, options = listOf(\n    selectOption(\"A\", label = \"A\") { println(\"Option Handler A\") }, //Option Handlers are called fist\n    selectOption(\"B\", label = \"B\") { println(\"Option Handler B\") },\n    selectOption(\"C\", label = \"C\") //You don't have to provide an option handler\n)) { //You can add a handler to the entire select (not required)\n    println(\"Select Handler: \" + event.values)\n}\n```\n\n\u003e [!NOTE]\n\u003e Select menus in message menus always have to be wrapped inside an `actionRow`. Since a select menu already takes up the entire action row, you cannot add any other components to that action row.\n\nFor entitySelects you can use the generic `entitySelect` or the more specific `userSelect`, `roleSelect`, `mentionableSelect` and `channelSelect`.\n\n### Components V2\nDiscordToolKit supports discords new `Components V2 Feature`. To enable it you can either set the global variable `DEFAULT_COMPONENTS_V2` to `true` or manually pass `useComponentsV2 = true` to your registerMenu call.\nThe variable `DEFAULT_COMPONENTS_V2` also defaults to `MessageReference.isDefaultUseComponentsV2()` so you could also change that if you want to enable it globally for all messages sent by your JDA instance.\n\nWhen a menu uses components v2, it cannot have regular contents like `content` or `embeds`. Calling functions to configure such fields on a menu with components v2 will throw exceptions when rendering the menu (because discord does not allow such messages).\nInstead, your message content should be displayed using text displays. You can create these using the `textDisplay` factory function. You can also use `localizedTextDisplay` to create a text display with localized content.\n\nAll other components also have factory functions, like `container`, `section`, `thumbnail`, `fileDisplay` and `mediaGallery`. They can be nested as supported by discord. Example:\n```kt\nregisterMenu(\"menu\", useComponentsV2 = true) {\n  var count by state(0)\n  var text by state(\"\")\n\n  +container(color = 0x00FF00) {\n      +section(\n          modalButton(\n              \"text\", label = \"Modal\", title = \"Enter Text\", \n              component = textInput(\"text\", placeholder = \"Hello World!\", value = text).withLabel(\"Enter Text\")\n          ) {\n              text = it\n          }\n      ) {\n          +textDisplay(\"Current Text: $text\")\n          +textDisplay(\"Current Count: **$count**\")\n      }\n    \n      +separator()\n      +actionRow {\n          +button(\"inc\", label = \"+\", color = ButtonColor.GREEN) { count++ }\n          +button(\"dec\", label = \"-\", color = ButtonColor.RED) { count-- }\n      }\n  }\n}\n```\nLocalization is also supported as usual.\n\n### Submenus and Menu components\nIn a lot of scenarios you might want to have some sort of submenu, for example a sub-category for a configuration menu. \nWhile you could simply create a new root menu and switch between those, managing state manually would be rather complicated.\n\n\u003e [!CAUTION]\n\u003e The following is a negative example on how you could achieve submenus when you don't actually use the submenu feature.\n\u003e While this might be useful in some scenarios, doing this is very error-prone and should be avoided.\n\n```kt\nval menu = registerMenu\u003cUnit\u003e(\"menu\") {\n    var parentState by state(0)\n    \n    //...\n    \n    +actionRow(button(\"child\", label = \"Child\") { switchMenu(getMessageMenu(\"submenu\")) {\n        copyAll() //Copy all state values to the child menu\n        pushDefaults() //USe default child state values for remaining state\n        \n        //copyAll(); pushDefaults() is the default state builder. So if you want to have this behavior you can also just not pass any value (See back button below)\n        //You can also have more advanced state transfers. See examples below\n    } })\n}\n\nregisterMenu\u003cUnit\u003e(\"submenu\") {\n    skipState(1) //Increase internal state id counter to keep space for the parent menu states\n\n    var childState by state(0)\n\n    //...\n\n    +actionRow(button(\"back\", label = \"Back\") { switchMenu(menu) })\n}\n```\nThe above example shows how the parent menu can switch to a different menu while still preserving the parent state. For example, changing the parent state to a value of 5, then going to the child menu, and then back again will keep the parent state at value 5.\nThis works because the parent state value is stored in the child state as well. If you want to have access to the parent state value inside the child menu you could do `val parent by state(0)` instead of `skipState(1)`.\n\nHowever, this manual state transfer might be hard to read. Therefore, you can use the `submenu` function to create a submenu directly as child menu. This will manage some of this automatically:\n\n```kt\nregisterMenu\u003cUnit\u003e(\"menu\") {\n    var parent by state(0)\n\n    content(\"Parent: $parent\")\n    \n    //Will be registered as `menu.submenu`\n    val submenu = submenu(\"submenu\") { back -\u003e\n        var child by state(0)\n        \n        content {\n            +line(\"Parent: $parent\")\n            +line(\"Child: $child\")\n        }\n      \n        +actionRow( \n          //Parent states are automatically inherited, and you can use the parent state accessors\n          //All changes to parent states will be preserved when going back to the parent menu. Child states will be lost because they only exist in the child menu\n          button(\"p-inc\", label = \"Parent +\") { parent++ },\n          button(\"c-inc\", label = \"Child +\") { child++ },\n\n          //The parameter provides an easy-to-read way to create a back button (\n          back.asButton(\"back\", label = \"Back\")   \n        )\n    }\n    \n    +actionRow(\n      button(\"inc\", label = \"+\") { parent++ },\n\n      button(\"child\", label = \"Child\") { switchMenu(submenu) },\n      button(\"child5\", label = \"Child with value\") { switchMenu(submenu) { copyAll(); push(5) } } //Use 5 as initial value for the child state   \n    )\n}\n```\n\nTo make this work, the parent config also has to be executed on each child render. \nIf you have expensive rendering in the parent, you can put it inside a `render` block. Parent render blocks will NOT be executed when rendering a child menu. Only states and localization context is inherited (See [Menu Localization](#menu-localization))\n\nIf you don't need the parent state values inside a submenu, but also need a lot of state space for child states, you can mark a submenu as detached (pass `detached = true` as parameter).\nThis will prevent the submenu from inheriting states from the parent. \nIt also prevents the parent config to be executing when rendering the child, so it is also a way to avoid performance overhead if you experience any problems with that (Even though that shouldn't be a problem in most cases, provided you put expensive rendering inside a `render` block).\n\nAs seen above, submenus are often only used to be opened by a single component. To make this process even simpler, there are some components available that can combine some of the steps above:\n```kt\nregisterMenu\u003cUnit\u003e(\"menu\") {\n    val submenu = submenu(\"submenu\") { back -\u003e\n        //...\n    }\n    \n    //Button that switches to the specified menu. Useful if you don't want to manually write the switchMenu handler but want to use the same submenu from different components (e.g. with different initial states). Also has an optional state builder parameter.\n    val a = switchMenuButton(submenu, label = \"Manual Submenu\")\n    \n    //Combine submenu declaration and component for switching to it\n    val b = menuButton(\"submenu-button\", label = \"Submenu Button\") { back -\u003e\n        //You can do everything you can do in a submenu config (define states, use parent states, etc.)\n        \n        //...\n    }\n  \n    +actionRow(a, b)\n}\n```\nThe most relevant components for this are `menuButton` and `menuSelectOption`.\n\nYou can find more examples in `src/test/kotlin/examples/ComplexMenu.kt`.\n\n### Pagination\nThere are some functions available that simplify the process of creating paginated menus. \nTo be more specific, there is a `pageSelector` component available, that allows users to freely switch between different pages. \nIf you need a more specific kind of pagination, you have to implement that yourself. This can be achieved using a page state and conditional message content.\nIf your pages have strongly varying components, you can also consider creating submenus for your pages.\n\nA very basic pagination can look like this:\n```kt\nregisterMenu\u003cUnit\u003e(\"paginate\", defer = DeferMode.UNLESS_PREVENTED) {\n    val pageRef = state(1)\n    var page by pageRef\n\n    content(if (page == 1) \"First Page\" else \"Second Page\")\n\n    +pageSelector(\"page\", max = 2, ref = pageRef)\n}\n```\n![](assets/paginate1.gif)\n\nThe button in the middle displays the current page and the page count. Clicking it opens a modal that allows to enter a page.\nYou can set the modal title and input label with the `title` and `label` parameters. To make this work, you have to set the defer mode to anything except `DeferMode.ALWAYS` because otherwise it would be impossible to reply with a modal.\n\nIn many cases you might want to list out a bunch of entries and paginate them into batches of a fixed size (e.g. to list a users collectibles):\n```kt\n//Example list of entries. Can also be generated inside the menu based on state\nval entries = (0 until 100).map { it % 15 + 1 }.map { Character.forDigit(it, 16).toString().repeat(it) }\n\nregisterMenu\u003cUnit\u003e(\"paginate\", defer = DeferMode.UNLESS_PREVENTED) {\n    val page = state(1)\n  \n    //This function returns both the pageSelector component and the current batch of entries that should be renderer\n    val (content, component) = pagination(\"page\", entries.toList(), display = { index -\u003e text(\"$index. \") + bold(this) }, perPage = 10, ref = page)\n\n    content(content) //You could also use this as part of an embed description\n\n    +component\n}\n```\n\nThis menu will paginate 100 entries on 10 pages: ![](assets/paginate2.gif)\n\n\u003e [!NOTE]\n\u003e Because a page select already shows 5 buttons, the `pageSelector` will already wrap them in an action row, so you can add it to a menu directly.\n\n### Text Builders\ndiscordToolKit provides a functional style message builder that allows you to create markdown formatted messages in a functional style. For example:\n```kt\nval data: MessageEditData = message {\n    content {\n        +h1(\"Heading\")\n        \n        +line(\"Abc\")\n        +line {\n            +bold(\"Abc: \")\n            +text(\"**Automatically __escaped__ markdown**\")\n        }\n        \n        +raw(\"**Manual __Markdown__**\\n\")\n        \n        +sub(\"Subtext\")\n    }\n    \n    embed(embed {\n        title(\"url\")\n        \n        //Also supports description { ... } like content above\n        description(\"description\")\n        \n        //... other embed fields\n        \n        image(\"some url\")\n        \n        //You can also directly add a buffered image. It will automatically be added as attachment:\n        image(bufferedImage)\n    })\n}.build()\n```\n\nThe main use case for this are message menus, because the `this` context in config blocks inherits from the message builder. \nThis means, that the message content, embeds and attachments of message menus have to be configured like this.\n\n\u003e [!NOTE]\n\u003e If you don't like this style, you can still just use `content(\"\u003ccontent\u003e\")`.\n\n## Modal Menus\nModal menus represent discord modals. You can use `registerModal` to create a new modal menu:\n```kt\nregisterModal\u003cUnit\u003e(\"modal\") {\n    //...\n}\n```\n\nA simple modal with a single text input could be implemented like this:\n```kt\nregisterModal\u003cUnit\u003e(\"modal\") {\n    val value by +textInput(\"text\").withLabel(\"Text\")\n    \n    execute {\n        println(value())\n    }\n}\n```\n\n\u003e [!NOTE]\n\u003e You always have to wrap components in a `label` to be able to add them to a modal menu. There is also the `withLabel` extension function that makes the component creation more readable.\n\n\u003e [!NOTE]\n\u003e Discord currently only allows `textInput`s, `stringSelect`s, `entitySelect`s and checkbox components inside modals. For select menus, their main handler (and option handlers) are NOT executed. Instead, the `modalHandler` is used.\n\nYou can create text inputs with the `textInput` function. They can be added to your modal with the `+` operator. Adding a text input will return a function that can be used inside the executor block to read the inputs value.\nThe `execute` block is what is executed when a user submits a modal.\n\nIn addition to `textInput` there are also the `checkbox`, `checkboxGroup` and `radioGroup` components, that are exclusive to modals. They also have factory functions as usual.\nWhile discord itself does not support required checkboxes, discord tool kit provides a `requiredCheckbox` function, that creates a required checkbox group with a single option under the hood.\nThis is useful for additional confirmation steps, since users cannot submit the modal unless they check the box.\n\nIf you only want to use a modal to read a text input from the user in a message menu, you can use `modalButton` (or `modalSelectOption`):\n```kt\nregisterMenu\u003cUnit\u003e(\"menu\", deferMode = DeferMode.UNLESS_PREVENTED) {\n    var name by state(\"\")\n    \n    content(\"**Name:** $name\")\n  \n    +modalButton(\"modal\", label = \"Change Name\", title = \"Enter Name\", component = \n        textInput(\"name\", value = name).withLabel(\"Your Name\")\n    ) { value -\u003e\n        name = value\n    }\n}\n```\nAs always with modals, a menu that might open a modal has to use a defer mode other than `DeferMode.ALWAYS`.\n\nIf you want to use a modal button with multiple inputs, you can combine multiple text inputs to a single modal component:\n```kt\nregisterMenu\u003cUnit\u003e(\"menu\", deferMode = DeferMode.UNLESS_PREVENTED) {\n    var name by state(\"\")\n    \n    content(\"**Name:** $name\")\n\n    +modalButton(\"modal\", title = \"Enter Name\", component = createModalComponent {\n        val current = name.split(\" \", limit = 2) \n        \n        val first by +textInput(\"first\", value = current[0]).withLabel(\"First Name\")\n        val last by +textInput(\"last\", value = if (current.size == 1) \"\" else current[1]).withLabel(\"Last Name\")\n\n        produce { \"$first $last\" }\n    }) { value -\u003e\n        name = value\n    }\n}\n```\nYou could also store the first name and last name in separate states by returning `listOf(first, last)` in the producer and changing the rest of the code respectively.\n\nAs with message submenus, opening a modal with a `modalButton` will preserve all parent state values.\n\n## MenuCommand\nIn most scenarios, you want to send a menu as a response to a command. To avoid having to register the menu separately in the ui manager configuration block or in a setup block of your command, and then replying with it manually, you can directly use a `menuCommand` that combines these steps:\n```kt\nmenuCommand(\"menu\") {\n    var state by state(0)\n    content(\"Count: $state\")  \n  \n    +actionRow(button(\"inc\", label = \"+\") { state++ })\n}\n```\nInside the menu command config block you can configure both the menu (as seen above) and the command itself:\n```kt\nmenuCommand(\"menu\") {\n    val option = option\u003cInt\u003e(\"option\").get()\n  \n    before {\n        //You can access option values inside before blocks.\n        //These are executed before the menu is opened.\n        //You can call terminateCommand to prevent anything after that to happen, including to open the menu\n        \n        if (option() == 0) terminateCommand()\n      \n        //However, you cannot initialize any state values in here because there is no menu yet during the runtime of this block.\n        //Use initialize for that\n    }\n\n    initialize { context -\u003e\n        //The initialize block allows you to modify the state values of the menu before it is sent. \n        //You can also access option values here, however, you have to pass the parameter of this block as parameter to you option getter.\n        \n        state = option(context)\n    }\n  \n    var optionState by option\u003cInt\u003e(\"state\").orElse(0).createState()\n\n    content(\"Count: $optionState\")\n    +actionRow(button(\"inc\", label = \"+\") { optionState++ })\n}\n```\nYou can define options as you would in a normal slash command (See [Options](#options) for more information). You can also create a state from an option using `createState`.\nThis will define a state will automatically be initialized with the value of the option. However, it is required that there is always a value available for the state, even during the builder phase.\nIf you have a non-required option (like above) and already provide a default value using `orElse`, this value will be used, and you are good to go. \nOtherwise, you have to provide a default value for the state as parameter in the `createState` method.\n\n# Localization\nDocumentation not yet available, however there are some examples at `examples/src/main/kotlin/examples/LocalizedCommand.kt` and `examples/src/main/kotlin/examples/LocalizedMenu.kt` that should be enough to see the basic concept.\n\nDiscordToolKit itself only provides interfaces for the actual LocalizationFile. The recommended way to actually create implementations for those is the [DiscordToolKitLocalizationPlugin](https://github.com/MineKing9534/DiscordToolKitLocalizationPlugin).\nIt provides a gradle plugin that integrates a kotlin symbol processor into your build that generates LocalizationFile implementations at compile time. \nThe localized values are read from yaml files at the specified location (Look at the example setup or the Readme in the plugin repository for more details).\n\nFor automatic localization for commands and menus, localization keys are automatically generated based on their names. You can override those by passing strings to the respective fields, e.g. passing a string as command description in the builder.\nIf a value is provided, instead of localization, this value is used as literal. If you want to use it as the localization key, you can use the `localize` extension function for strings to mark it as localization key.\n\nTo use localization, you have to specify the file to read the localization from. The examples mentioned above show how to do that. You can also override the localization file for certain sub-parts like options or components by passing the file to use as the `localization` parameter.\nThis can be useful if you want to define a custom option that is used in several places. Using a custom localization allows you to define a global file to use for this option's localization instead of having to define its description in the localization of very respective command.\n\n## General\n## Command Localization\n## Menu Localization","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmineking9534%2Fdiscordtoolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmineking9534%2Fdiscordtoolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmineking9534%2Fdiscordtoolkit/lists"}