{"id":20349607,"url":"https://github.com/mineking9534/discordutils","last_synced_at":"2025-04-12T01:24:12.633Z","repository":{"id":171987938,"uuid":"648675677","full_name":"MineKing9534/DiscordUtils","owner":"MineKing9534","description":"A utility library for discord bot development with JDA","archived":false,"fork":false,"pushed_at":"2024-10-13T16:51:50.000Z","size":551,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T21:11:20.193Z","etag":null,"topics":["bot","commands","discord","jda"],"latest_commit_sha":null,"homepage":"","language":"Java","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}},"created_at":"2023-06-02T14:24:50.000Z","updated_at":"2024-10-13T16:51:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"e4a5a338-9f40-4177-8cf4-25b32b4a29ca","html_url":"https://github.com/MineKing9534/DiscordUtils","commit_stats":null,"previous_names":["minekingbot/discordutils","utils4j/discordutils"],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordUtils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordUtils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordUtils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MineKing9534%2FDiscordUtils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MineKing9534","download_url":"https://codeload.github.com/MineKing9534/DiscordUtils/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248502422,"owners_count":21114810,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["bot","commands","discord","jda"],"created_at":"2024-11-14T22:26:44.324Z","updated_at":"2025-04-12T01:24:12.604Z","avatar_url":"https://github.com/MineKing9534.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"![[Java CI]](https://github.com/MineKing9534/DiscordUtils/actions/workflows/check.yml/badge.svg)\n![[Latest Version]](https://maven.mineking.dev/api/badge/latest/releases/de/mineking/DiscordUtils?prefix=v\u0026name=Latest%20Version\u0026color=0374b5)\n\n# Installation\n\nDiscordUtils is hosted on a custom repository at [https://maven.mineking.dev](https://maven.mineking.dev/#/releases/de/mineking/DiscordUtils). Replace VERSION with the lastest version (without the `v` prefix).\nAlternatively, you can download the artifacts from jitpack (not recommended).\n\n### Gradle\n\n```groovy\nrepositories {\n  maven { url \"https://maven.mineking.dev/releases\" }\n}\n\ndependencies {\n  implementation \"de.mineking:DiscordUtils:VERSION\"\n}\n```\n\n### Maven\n\n```xml\n\u003crepositories\u003e\n  \u003crepository\u003e\n    \u003cid\u003emineking\u003c/id\u003e\n    \u003curl\u003ehttps://maven.mineking.dev/releases\u003c/url\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003ede.mineking\u003c/groupId\u003e\n    \u003cartifactId\u003eDiscordUtils\u003c/artifactId\u003e\n    \u003cversion\u003eVERSION\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n### Dependencies\n\nDiscordUtils requires [JDA](https://github.com/discord-jda/JDA), so you have to have it in your bot's dependency list as well (You should already have it).\nSome managers might require other dependencies, that you have to add to your project in order for that manager to work.\nThese additional dependencies are mentioned in the section of the corresponding manager.\n\n# Basic Usage\n\nTo start using DiscordUtils you can simply create an instance of `DiscordUtils` with the first constructor parameter being your `JDA` instance and the second one your bot class instance.\n\nExample:\n\n```java\npublic class TestBot {\n  public static void main(String[] args) {\n    new TestBot(args[0]);\n  }\n\n  public final JDA jda;\n  public final DiscordUtils discordUtils;\n\n  public TestBot(String token) {\n    jda = JDABuilder.createDefault(token)\n            .build();\n\n    discordUtils = DiscordUtils.create(jda, this)\n            .build();\n  }\n}\n```\n\nYou can then later access your bot instance from any place where DiscordUtils gives you access to the `DiscordUtils` instance, which helps to avoid static abuse.\n\n# Localization\n\nYou can enable localization by calling the `setLocalizationManager` method on your `DiscordUtils` instance.\nYou can provide a default locale that will be set as default value and a set of locales that you support.\nThe third parameter is a `LocalizationFunction` that you can use to provide a localized description for a localization path.\nYou can also change how the paths are generated automatically.\n\n# Console\n\nSometimes, you might want to mirror your bot's console to your admins' direct messages or a text channel. DiscordUtils provides a simple way to archive this.\n\n```java\npublic class TestBot {\n  public static void main(String[] args) {\n    new TestBot(args[0]);\n  }\n\n  public final JDA jda;\n  public final DiscordUtils discordUtils;\n\n  public TestBot(String token) {\n    jda = JDABuilder.createDefault(token)\n            .build();\n\n    discordUtils = DiscordUtils.create(jda, this)\n            .mirrorConsole(RedirectTarget.directMessage(YOUR_USER_ID)) //Mirror the console to you dms\n            .build();\n  }\n}\n```\n\nTo send the logs to a text channel, simply use `RedirectTarget.channel(CHANNEL_ID)`. You can also provide multiple RedirectTargets at once.\n\n# Managers\n\nThe DiscordUtils library is split into multiple Managers to allow you to decide which features you want to use.\nYou can either manually register a Manager with `addManager` or with the corresponding method for your Manager.\nThese dedicated methods start with `use`, to use the CommandManager your can use `useCommandManager`.\n\nTo get a previously registered manager by type, you can manually get it via `getManager` that holds the manager if present or `Optional.empty()` otherwise.\nAlternatively you can use the dedicated method, for example `getCommandManager`.\nThe dedicated methods throw an `IllegalStateException` if the requested manager is not present.\n\nAvailable Managers:\n\n- [CommandManager](#command-manager)\n- [Language cache](#language-cache)\n- [Event Manager](#event-manager)\n- [UI Manager](#ui-manager)\n- [Help Manager](#help-manager)\n- [List Manager](#list-manager)\n\n## Command Manager\n\nThe CommandManager can be used to simplify the process of registering and handling discord application commands.\nYou can register the CommandManager by calling `useCommandManager` on your `DiscordUtils` instance.\nThe `useCommandManager` method takes three parameters: A function to create a Context for command execution, a function to create a Context for autocomplete and a consumer that can be used to configure the newly created `CommandManager`.\n\nExample:\n\n```java\n//An instance of this context that will be created using the context creator function that you provided when creating the CommandManager, will be passed to every command execution. You can customize the context to your needs\npublic class CommandContext implements ICommandContext {\n  private final GenericCommandInteractionEvent event;\n\n  public CommandContext(GenericCommandInteractionEvent event) {\n    this.event = event;\n  }\n\n  public GenericCommandInteractionEvent getEvent() {\n    return event;\n  }\n\n  //Add methods like 'reply' that use your bot's localization system\n\n  //If you have a moderation bot, you could add methods that give access to the executing users warnings so that you can do context.getWarnings() wherever you need\n}\n\npublic class AutocompleteContext implements IAutocompleteContext {\n  private final CommandAutocompleteEvent event;\n\n  public AutocompleteContext(CommandAutocompleteInteractionEvent event) {\n    this.event = event;\n  }\n\n  public CommandAutocompleteEvent getEvent() {\n    return event;\n  }\n}\n\npublic class TestBot {\n  public static void main(String[] args) {\n    new TestBot(args[0]);\n  }\n\n  public final JDA jda;\n  public final DiscordUtils discordUtils;\n\n  public TestBot(String token) {\n    jda = JDABuilder.createDefault(token)\n            .build();\n\n    discordUtils = DiscordUtils.create(jda, this)\n            .useCommandManager(\n\t\t            CommandContext::new, //Function to create command context\n\t\t            AutocompleteContext::new, //Function to create autocomplete context\n                    cmdMan -\u003e cmdMan.updateCommands() //Consumer to configure the resulting CommandManager. The updateCommands() method schedules an update of all commands for when the bot is successfully logged in\n            ).build();\n  }\n}\n```\n\nTo actually create your commands, there are two ways: inherited and annotated. In most cases, it is recommended to use annotated commands because they are a lot easier to create and the resulting code is a lot more readable.\n\n### Annotated\n\nHere is a basic example of an annotated command:\n\n```java\n\n@ApplicationCommand(name = \"echo\", description = \"sends back the the input\") //The @ApplicationCommand annotation specifies basic information about your command. It has to be present!\npublic class EchoCommand {\n  @ApplicationCommandMethod //The @ApplicationCommandMethod specifies the method that actually handles the command execution\n  public void performCommand(CommandContext context, //The method can have any name\n                             @Option(name = \"text\") String text //All method parameters with the @Option annotation will be added to the command's option list. They will automatically be created and parsed\n  ) {\n    context.event.reply(text).setEphemeral(true).queue();\n  }\n}\n```\n\nIf your option should have the same name on discord as your method parameter, you can provide no name in the `@Option` annotation.\nDiscordUtils will then use the method parameters name. In order for this to function as expected, you have to compile your bot with the `-parameters` option.\nIn Gradle, you can archive this by adding this to `build.gradle`:\n\n```groovy\ncompileJava {\n  options.compilerArgs \u003c\u003c '-parameters'\n}\n```\n\nYou can make options non-required by adding `required = false` in `@Option`. If no value for an optional option is provided, it will be `null`.\nThis means that you have to use the wrapper classes for primitive types to allow for nullability.\nSo instead of `@Option(required = false) int x` you have to use `@Option(required = false) Integer x`.\nAlternatively, you can wrap any type in an `Optional`. You can then simply handle them like this:\n\n```java\n\n@ApplicationCommand(name = \"test\")\npublic class TestCommand {\n  @ApplicationCommandMethod\n  public void performCommand(@Option(required = false) Optional\u003cString\u003e text) {\n    text.ifPresentOrElse(\n            value -\u003e {/* handle value */},\n            () -\u003e {/* handle missing option */}\n    );\n  }\n}\n```\n\nIf you want to use a default value, you can add the corresponding default annotation for your type.\nExample:\n\n```java\n\n@ApplicationCommand(name = \"test\")\npublic class TestCommand {\n  @ApplicationCommandMethod\n  public void performCommand(@BooleanDefault(false) @Option(required = false) boolean flag) {\n    //flag will never be null. If the option is not provided, it will be 'false' as specified by the annotation parameter.\n  }\n}\n```\n\nIf you want to limit the possible values for an option to specific choices, you can register these choices the following way:\n\n```java\npublic class TestCommand {\n  @Choice(\"value\")\n  public List\u003cChoice\u003e choices = Arrays.asList( //These will be the only choices for option 'value'\n          new Command.Choice(\"name a\", \"value a\"),\n          new Command.Choice(\"name b\", \"value b\"),\n          new Command.Choice(\"name c\", \"value c\")\n  );\n\n  @ApplicationCommandMethod\n  public void performCommand(@Option String value) {\n\n  }\n}\n```\n\nWhen dealing with choices, you might also have an enum that represents your possible choices. In DiscordUtils, you can do:\n\n```java\npublic class TestCommand {\n  public enum Test {\n    A,\n    B,\n    C\n  }\n\n  @ApplicationCommandMethod\n  public void performCommand(@Option Test value) { //When using an enum as option type, only the enum entries are allowed as choices\n\n  }\n}\n```\n\nIf you want to dynamically create your choices based on the user input, you can use autocomplete. However, du to a discord limitation, autocomplete choices are not enforced:\n\n```java\npublic class TestCommand {\n  @Autocomplete(\"value\")\n  public void handleAutocomplete(AutocompleteContext context) {\n    context.event\n            .replyChoice(context.event.getFocusedOption().getValue() + \"!\", context.event.getFocusedOption().getValue() + \"!\") //Suggest adding an exclamation mark at the end of the current input\n            .queue();\n    //If you want to access other command information like registered execution conditions, you can add a method parameter of type 'Command\u003c?, ?\u003e'. From there you can access all data about the current command that you want\n  }\n\n  @ApplicationCommandMethod\n  public void performCommand(@Option String value) {\n\n  }\n}\n```\n\nSometimes, you may want to ask the user for an array of options. Unfortunately, Discord doesn't allow us to do so. However, DiscordUtils has a clever trick to simulate similar behavior:\n\n```java\npublic class TestCommand {\n  @ApplicationCommandMethod\n  public void performCommand(@OptionArray(minCount = 2, maxCount = 5) @Option String[] value) { //You can either use an array or a List. As component type, every type is supported\n    //This will create 5 options, where 2 are required and 3 are not required. Their name will be 'value1', 'value2', etc.\n  }\n}\n```\n\nYou can also register parsers to use your own types as a parameter type. In fact, all types that are supported by default are implemented like this.\nFor example, if you want to use a color as a parameter, you could write the following option parser:\n\n```java\npublic class ColorParser implements IOptionParser {\n  @Override\n  public boolean accepts(@NotNull Class\u003c?\u003e type) {\n    return type.isAssignableFrom(Color.class); //Check whether the type is Color\n  }\n\n  @NotNull\n  @Override\n  public OptionType getType(@NotNull CommandManager\u003c?, ?\u003e manager, @NotNull Class\u003c?\u003e type, @NotNull Type generic) {\n    return OptionType.STRING; //We will be using hex codes to represent the color, which means it will be a string\n  }\n\n  @Nullable\n  @Override\n  public Object parse(@NotNull CommandManager\u003c?, ?\u003e manager, @NotNull GenericCommandInteractionEvent event, @NotNull String name, @NotNull Parameter param, @NotNull Class\u003c?\u003e type, @NotNull Type generic) {\n    try {\n      return event.getOption(name, o -\u003e Color.decode(o.getAsString())); //Parse color\n    } catch(NumberFormatException e) {\n      return null; //Set the parameter to null if the provided color was not valid\n    }\n  }\n\n  @Override\n  public void registerOption(@NotNull de.mineking.discordutils.commands.Command\u003c?\u003e cmd, @NotNull OptionData option, @NotNull Parameter param) {\n    //The color will be in the format #rrggbb, so the length of the string is 7\n    cmd.addOption(option.setMinLength(7).setMaxLength(7)); //Customize option\n  }\n}\n```\n\nIn this case, both the `accepts` and `getType` method implementations are very simple. In this case, you can make use of the `OptionParser` class like this:\n\n```java\npublic class ColorParser extends OptionParser {\n  public ColorParser() {\n    super(Color.class, OptionType.STRING); //Use super constructor\n  }\n\n  @Nullable\n  @Override\n  public Object parse(@NotNull CommandManager\u003c?, ?\u003e manager, @NotNull GenericCommandInteractionEvent event, @NotNull String name, @NotNull Parameter param, @NotNull Class\u003c?\u003e type, @NotNull Type generic) {\n    try {\n      return event.getOption(name, o -\u003e Color.decode(o.getAsString())); //Parse color\n    } catch(NumberFormatException e) {\n      return null; //Set the parameter to null if the provided color was not valid\n    }\n  }\n\n  @Override\n  public void registerOption(@NotNull de.mineking.discordutils.commands.Command\u003c?\u003e cmd, @NotNull OptionData option, @NotNull Parameter param) {\n    //The color will be in the format #rrggbb, so the length of the string is 7\n    cmd.addOption(option.setMinLength(7).setMaxLength(7)); //Customize option\n  }\n}\n```\n\nYou have to register your option parsers with `CommandManager#registerOptionParser`.\n\nIf you want to add subcommands, you can add nested classes to your main command class. These will automatically be used as subcommands:\n\n```java\n\n@ApplicationCommand(name = \"config\")\npublic class ConfigCommand {\n  @ApplicationCommand(name = \"permission\") //Will automatically be registered as subcommand\n  public static class PermissionCommand {\n\n  }\n}\n```\n\nBecause discord only supports three command layers (command, subcommand group, subcommand), commands with higher levels will not be registered as official subcommands, but instead they will be represented with a '_' delimiter.\nFor example, a structure like this will be created for subcommands in layer 4 or above:\n\n```md\n- config permission list\n- config permission add_user\n- config permission remove_user\n```\n\nThese underscores are automatically added by DiscordUtils for the discord representation. However, internally they still behave like layer four commands.\n\nIf you want to manually change some configuration of your command like adding an inherited command as subcommand, you can create a method with the `@Setup` annotation.\nAll methods with this annotation will be called when creating the command, giving you access to the play `Command` object that represents your command:\n\n```java\n\n@ApplicationCommand(name = \"test\")\npublic class TestCommand {\n  @Setup\n  public void setup(Command\u003c?, ?\u003e cmd) {\n    cmd.addSubcommand(new InheritedSubcommand());\n  }\n}\n```\n\nIn all the above examples, DiscordUtils automatically creates an instance of your command class and executes all methods on that instance.\nIf you want to execute the command on a dynamic instance, you can use the following registration method:\n\n```java\ncommandManager.registerCommand(TestCommand.class,\n        commandContext-\u003e{}, //Return the instnace you want to use based on the command context here\n        autocompleteContext-\u003e{} //Return the instance you want to use based on the autocomplete context here\n        )\n```\n\nYou can also declare multiple commands in one class like this:\n\n```java\npublic class TestCommands {\n  @ApplicationCommand(name = \"a\")\n  public void performA(CommandContext context) {\n    //...\n  }\n\n  @ApplicationCommand(name = \"b\")\n  public void performB(CommandContext context) {\n    //...\n  }\n}\n```\n\nYou can then register the commands bs providing `TestCommands.class` to `registerCommand`. Because the class is not annotated with `@ApplicationCommand`, DiscordUtils will automatically search for methods with the `@ApplicationCommand`\nannotation and use these instead.\n\n### Inherited\n\nTo create inherited commands, you can create a class that extends `Command`. You then have to create an instance of your command class and register it to the command manager:\n\n```java\npublic class TestCommand extends Command\u003cCommandContext\u003e {\n  public TestCommand() {\n    addOption(new OptionData(OptionType.STRING, \"test\", \"description\", true));\n  }\n\n  @Override\n  public void performCommand(CommandContext context) {\n    //You have to access the option like you would normally with context.event.getOption(...)\n  }\n}\n```\n\nTo register:\n\n```java\ncommandManager.registerCommand(new TestCommand());\n```\n\nFor autocomplete, you can use `AutocompleteOption`:\n\n```java\npublic class TestCommand extends Command\u003cCommandContext\u003e {\n  public TestCommand() {\n    addOption(new AutocompleteOption(OptionType.STRING, \"test\", \"description\", true) {\n      @Override\n      public void handleAutocomplete(AutocompleteContext context) {\n        context.event\n                .replyChoice(context.event.getFocusedOption().getValue() + \"!\", context.event.getFocusedOption().getValue() + \"!\") //Suggest adding an exclamation mark at the end of the current input\n                .queue();\n      }\n    });\n  }\n\n  @Override\n  public void performCommand(CommandContext context) {\n    //You have to access the option like you would normally with context.event.getOption(...)\n  }\n}\n```\n\nTo add subcommands, you can use the `addSubcommand` method in the command constructor. You can add both annotated and inherited commands as subcommands.\n\n## Language cache\n\nThe language cache manager can be used to access a user's locale from places, where you don't have access to it from Discord's side.\nThe manager listens to all interactions and caches the user locale field.\nThe cached values will expire 4 hours after the last access or write to the user's locale value.\nFor caching, the caffeine library is used.\nIf you want to use the language cache manager, you have to add this dependency to your project:\n\n```groovy\nimplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'\n```\n\n## Event Manager\n\nThe EventManager allows you to register event handlers in a more simple way. You can either write your own `EventHandler` or use one of the ones brought with DiscordUtils:\n\n- FilteredEventHandler\n- ComponentHandler\n- ButtonHandler\n- StringSelectHandler\n- EntitySelectHandler\n- ModalHandler\n  You can register your handlers with like this:\n\n```java\npublic class TestBot {\n  public static void main(String[] args) {\n    new TestBot(args[0]);\n  }\n\n  public final JDA jda;\n  public final DiscordUtils discordUtils;\n\n  public TestBot(String token) {\n    jda = JDABuilder.createDefault(token)\n            .build();\n\n    discordUtils = new DiscordUtils(jda, this)\n            .useEventManager(eventManager -\u003e {\n              eventManager.addEventHandler(\n                      new ButtonHandler(\"test\", event -\u003e {\n                        /* handle event */\n                      })\n              );\n              //...\n            });\n  }\n}\n```\n\nWhen you enable the EventManager, you can also use `@Listener` in annotated commands:\n\n```java\n\n@ApplicationCommand(name = \"test\")\npublic class TestCommand {\n  @ApplicationCommandMethod\n  public void performCommand(CommandContext context) {\n    context.event.replyModal(\n            Modal.create(\"test:modal\", \"title\", TextInputStyle.SHORT)\n                    //...\n                    .build()\n    ).queue();\n  }\n\n  @Listener(type = ModalHandler.class, filter = \"test:modal\") //Handle modals with id \"test:modal\"\n  public void handleModal(ModalInteractionEvent event) {\n    //Handle your modal\n  }\n}\n```\n\n## UI Manager\n\nThe UIManager allows you to create complex menus without having to manually create event handlers and manage states.\nBefore you use `useUIManager` you have to call `useEventManager` because the UIManager uses the EventManager internally.\nThe UIManager requires this additional dependency:\n\n```groovy\nimplementation 'com.google.code.gson:gson:2.10.1'\n```\n\nExample:\n\n```java\n\n@ApplicationCommand(name = \"test\")\npublic class TestCommand {\n  public final MessageMenu menu;\n\n  public UITestCommand(UIManager manager) {\n    menu = manager.createMenu(\n            \"test\",\n            MessageRenderer.embed(state -\u003e new EmbedBuilder()\n                    .setTitle(\"Test Menu\")\n                    .addField(\"Text\", state.getState(\"text\"), false)\n                    .addField(\"Last user\", state.getEvent().map(e -\u003e e.getUser().toString()).orElse(\"*none*\"), false)\n                    .build()\n            ),\n            ComponentRow.of(\n                    new ButtonComponent(\"button\", ButtonColor.BLUE, \"Append !\")\n                            .appendHandler(state -\u003e {\n                              state.setState(\"text\", current -\u003e current + \"!\");\n                              state.update();\n                            }),\n                    new ToggleComponent(\"toggle\", state -\u003e state ? ButtonColor.GREEN : ButtonColor.RED, \"Toggle\")\n            )\n    ).\u003cBoolean\u003eeffect(\"toggle\", value -\u003e System.out.println(\"Toggle value changed: \" + value));\n  }\n\n  @ApplicationCommandMethod\n  public void performCommand(CommandContext context) {\n    menu.createState()\n            .setState(\"text\", \"abc\")\n            .setState(\"toggle\", true)\n            .display(context.event, false);\n\n  }\n}\n```\n\nThe `state` represents the current state of the menu display. A menu can be displayed multiple times at a time, and all displays are restart-persistent.\nAs the example shows, the java instance of `Menu` should only be created once, and you can then display the menu multiple times via that one instance.\nIf you don't need to set an initial state like in the above example, you can also call the `display` method directly on the `Menu` instance.\n\nThe state is stored as json in the component ids. This means that the storage space in state is very limited, and you should only persist data in there that really have to be stored.\nFor example, you should not store large objects but instead an identifier to then load the actual data from a database or something like that:\n\nAdditionally, you should not store sensitive information in a state because other users or bots might be able to see the component custom ids, where the state is saved.\n\n```java\nstate.getState(\"xy id\", id -\u003e database.getFromId(id));\n```\n\nThe `effect` method can be used to detect state changes, much like in the react framework in web development. This may be useful if you want to store the value of a state in the database or something similar.\n\nAvailable Components:\n\n- [ButtonComponent](#buttoncomponent)\n- [ToggleComponent](#togglecomponent)\n- [MenuComponent](#menucomponent)\n- [StringSelectComponent](#stringselectcomponent)\n- [EntitySelectComponent](#entityselectcomponent)\n\n### ButtonComponent\n\nThe button component is the basic component. It represents a button under the message that you can configure.\nThe first parameter of all constructors is the name of the component. The name is used internally to identify the component. It has to be unique in one menu to avoid conflicts.\nWith the multiple available constructors, you can set the color and label of the button, either constant or dependent on the current state.\n\nYou can handle a button click by adding a handler. There are `appendHandler` and `prependHandler`. By choosing `append` or `prepend` you can modify the order of execution.\nAdditionally, DiscordUtils supports detecting double clicks. As soon as you register a double click handler, all normal click events are delayed.\nIf another click is executed in the next 3 seconds, a double click is triggered; otherwise a normal click event is fired.\nYou can change the 3-seconds delay with `setDoubleclickTimeout`.\n\n### ToggleComponent\n\nThe ToggleComponent is an extension for the button component. It behaves very similarly, but it adds a click handler by default.\nThe handler simply toggles between two states. The current toggle state is stored in a state with the same name as the component.\nYou can add additional handlers or listen to state changes with `effect`.\n\n### MenuComponent\n\nThe MenuComponent is another extension for the ButtonComponent. When pressed, it displays a different menu. This mechanic can be used to have a menu with multiple frames.\nBy default, the current state is transferred to the new menu. To change this behavior, you can use `setStateCreator`.\n\n### StringSelectComponent\n\nThe StringSelectComponent is a component that allows users to select between multiple options. You can dynamically set these options. You can also set a minimum and a maximum for the options a user has to select.\n\n### EntitySelectComponent\n\nThe EntitySelectComponent is similar to StringSelectComponent, but instead of custom options, a user has to select between users, channels or roles. You can specify the entity type in the constructor.\n\n## Help Manager\n\n## List Manager\n\nThe ListManager can be used to paginate large amounts of data. It uses the [UIManager](#ui-manager) internally.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmineking9534%2Fdiscordutils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmineking9534%2Fdiscordutils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmineking9534%2Fdiscordutils/lists"}