Skip to content

Commit 7d4530e

Browse files
Tais993marko-radosavljevic
authored andcommitted
Add command-system
Added Command and AbstractCommand for commands to extend. Added CommandExample and SubCommandExample as examples/guides for people/animals that want to create a command. Added the CommandHandler that actually handles the command Added the ReloadCommand so you can reload commands Added the CommandHandler to the EventListeners in Application.java
1 parent 367dbfc commit 7d4530e

7 files changed

Lines changed: 665 additions & 0 deletions

File tree

application/src/main/java/org/togetherjava/tjbot/Application.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import net.dv8tion.jda.api.JDABuilder;
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
7+
import org.togetherjava.tjbot.commands.CommandHandler;
78
import org.togetherjava.tjbot.db.Database;
89

910
import javax.security.auth.login.LoginException;
@@ -53,6 +54,7 @@ public static void runBot(String token, Path databasePath) {
5354
JDA jda = JDABuilder.createDefault(token)
5455
.addEventListeners(new PingPongListener())
5556
.addEventListeners(new DatabaseListener(database))
57+
.addEventListeners(new CommandHandler())
5658
.build();
5759
jda.awaitReady();
5860
logger.info("Bot is ready");
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package org.togetherjava.tjbot.commands;
2+
3+
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
4+
import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
5+
import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
6+
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
7+
import net.dv8tion.jda.api.interactions.components.Component;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.togetherjava.tjbot.commands.example.CommandExample;
10+
import org.togetherjava.tjbot.commands.example.SubCommandExample;
11+
12+
import java.time.Instant;
13+
import java.util.Collection;
14+
import java.util.List;
15+
16+
/**
17+
* The class all commands have to abstract. <br>
18+
* Look at {@link CommandExample} for an example. <br>
19+
* Or look at {@link SubCommandExample} for an example with subcommands.
20+
*
21+
* <br>
22+
* <b> All commands that implement this interface need to be added command to the
23+
* {@link CommandHandler} class!</b>
24+
*/
25+
public interface Command {
26+
27+
/**
28+
* <p>
29+
* The command's name.
30+
*
31+
* <p>
32+
* <b>Requirements!</b>
33+
* <ul>
34+
* <li>Lowercase</li>
35+
* <li>Alphanumeric (with dash)</li>
36+
* <li>1 to 32 characters long</li>
37+
* </ul>
38+
*
39+
* @return The commands name as a {@link String}
40+
*/
41+
@NotNull
42+
String getCommandName();
43+
44+
/**
45+
* <p>
46+
* The command's description.
47+
*
48+
* <p>
49+
* <b>Requirements!</b>
50+
* <ul>
51+
* <li>1 to 100 characters long</li>
52+
* </ul>
53+
*
54+
* @return The command's description as a {@link String}
55+
*/
56+
@NotNull
57+
String getDescription();
58+
59+
/**
60+
* Whenever the command is only for guilds, optional method.
61+
*
62+
* @return Whenever the command is only for guilds as a {@link Boolean}
63+
*/
64+
default boolean isGuildOnly() {
65+
return false;
66+
}
67+
68+
/**
69+
* This command will add all the options to the slash-command. <br>
70+
* Look at {@link CommandExample#addOptions(CommandData)} for an example.
71+
*
72+
* @param commandData The {@link CommandData} where the options need to be added.
73+
* @return The changed {@link CommandData}
74+
*/
75+
default @NotNull CommandData addOptions(@NotNull CommandData commandData) {
76+
return commandData;
77+
}
78+
79+
/**
80+
* The execute method for when the command gets executed. <br>
81+
* Check <a href="https://github.com/DV8FromTheWorld/JDA/wiki/Interactions#slash-commands">JDA's
82+
* slash-commands Wiki article</a> for more information.
83+
*
84+
* @param event The relating {@link SlashCommandEvent}
85+
*/
86+
default void onSlashCommand(SlashCommandEvent event) {}
87+
88+
/**
89+
* The execute method for when a button related to this command gets clicked. <br>
90+
* Check <a href="https://github.com/DV8FromTheWorld/JDA/wiki/Interactions#buttons">JDA's
91+
* buttons Wiki article</a> for more information.
92+
*
93+
* @param event The relating {@link ButtonClickEvent}
94+
* @param idArgs All the arguments stored in the ID ({@link Component#getId()
95+
* Component#getId()}) have been converted to a {@link List} The commands name and
96+
* current time in MS have been removed from this, example:
97+
* "examplecommand-7272727272-userId", this will be converted to a {@link List}
98+
* containing "userId", the rest has been removed.
99+
*/
100+
default void onButtonClick(ButtonClickEvent event, List<String> idArgs) {}
101+
102+
/**
103+
* The execute method for when a selection menu related to this command gets clicked. <br>
104+
* JDA has no dedicated guide for selection menu's, they work the same as buttons. <br>
105+
* Check <a href="https://github.com/DV8FromTheWorld/JDA/wiki/Interactions#buttons">JDA's
106+
* buttons Wiki article</a> for more information.
107+
*
108+
* @param event The relating {@link ButtonClickEvent}
109+
* @param idArgs All the arguments stored in the ID ({@link Component#getId()
110+
* Component#getId()}) have been converted to a {@link List} The commands name and
111+
* current time in MS have been removed from this, example:
112+
* "examplecommand-7272727272-userId", this will be converted to a {@link List}
113+
* containing "userId", the rest has been removed.
114+
*/
115+
default void onSelectionMenu(SelectionMenuEvent event, List<String> idArgs) {}
116+
117+
118+
/**
119+
* Generates a {@link net.dv8tion.jda.api.interactions.components.Component Component} ID based
120+
* on the command name, current time and given arguments, <br>
121+
* {@link net.dv8tion.jda.api.interactions.components.Component Component} examples are
122+
* {@link net.dv8tion.jda.api.interactions.components.Button Buttons} and or
123+
* {@link net.dv8tion.jda.api.interactions.components.selections.SelectionMenu SelectionMenu's}
124+
*
125+
* <br>
126+
* An ID can be maximum of
127+
* {@link net.dv8tion.jda.api.interactions.components.Button#ID_MAX_LENGTH} chars. <br>
128+
* Between every argument an `{@code -}` is added, and by default the command name + current
129+
* time in MS gets added.
130+
*
131+
* @param args A {@link String} array of the arguments
132+
* @return A {@link String} generated ID in the format of
133+
* `{@code commandName-timeInMs-arg1-arg2}`
134+
*/
135+
default @NotNull String generateComponentId(@NotNull String... args) {
136+
StringBuilder stringBuilder = new StringBuilder(getCommandName());
137+
138+
stringBuilder.append("-").append(Instant.now().getNano());
139+
140+
for (String arg : args) {
141+
stringBuilder.append("-").append(arg);
142+
}
143+
144+
return stringBuilder.toString();
145+
}
146+
147+
/**
148+
* /** Generates a {@link net.dv8tion.jda.api.interactions.components.Component Component} ID
149+
* based on the command name, current time and given arguments, <br>
150+
* {@link net.dv8tion.jda.api.interactions.components.Component Component} examples are
151+
* {@link net.dv8tion.jda.api.interactions.components.Button Buttons} and or
152+
* {@link net.dv8tion.jda.api.interactions.components.selections.SelectionMenu SelectionMenu's}
153+
*
154+
* <br>
155+
* An ID can be maximum of
156+
* {@link net.dv8tion.jda.api.interactions.components.Button#ID_MAX_LENGTH} chars. <br>
157+
* Between every argument an `{@code -}` is added, and by default the command name + current
158+
* time in MS gets added.
159+
*
160+
* @param args A {@link Collection<String>} of {@link String}'s of the arguments
161+
* @return A {@link String} generated ID in the format of
162+
* `{@code commandName-timeInMs-arg1-arg2}`
163+
*/
164+
default @NotNull String generateComponentId(@NotNull Collection<String> args) {
165+
return generateComponentId(args.toArray(new String[] {}));
166+
}
167+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package org.togetherjava.tjbot.commands;
2+
3+
import net.dv8tion.jda.api.events.ReadyEvent;
4+
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
5+
import net.dv8tion.jda.api.events.interaction.GenericComponentInteractionCreateEvent;
6+
import net.dv8tion.jda.api.events.interaction.SelectionMenuEvent;
7+
import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
8+
import net.dv8tion.jda.api.hooks.ListenerAdapter;
9+
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
13+
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.function.BiConsumer;
19+
import java.util.stream.Collectors;
20+
21+
/**
22+
* The command handler
23+
*
24+
* Commands need to be added to the commandList
25+
*/
26+
public class CommandHandler extends ListenerAdapter {
27+
private final static Logger logger = LoggerFactory.getLogger(CommandHandler.class);
28+
29+
private final List<Command> commandList = new ArrayList<>();
30+
private final Map<String, Command> commandMap;
31+
32+
public CommandHandler() {
33+
commandList.addAll(List.of(new ReloadCommand(this)
34+
// add your command here
35+
));
36+
37+
commandMap = commandList.stream()
38+
.collect(Collectors.toMap(Command::getCommandName, command -> command));
39+
}
40+
41+
@Override
42+
public void onReady(@NotNull ReadyEvent event) {
43+
event.getJDA().getGuildCache().forEach(guild -> {
44+
guild.retrieveCommands().queue(commands -> {
45+
boolean hasReloadCommand =
46+
commands.stream().anyMatch(command -> command.getName().equals("reload"));
47+
48+
if (!hasReloadCommand) {
49+
Command command = commandMap.get("reload");
50+
guild.upsertCommand(command.addOptions(
51+
new CommandData(command.getCommandName(), command.getDescription())))
52+
.queue();
53+
}
54+
});
55+
});
56+
}
57+
58+
@Override
59+
public void onSlashCommand(@NotNull SlashCommandEvent event) {
60+
Command command = commandMap.get(event.getName());
61+
62+
if (command != null) {
63+
command.onSlashCommand(event);
64+
} else {
65+
logger.error("Command '{}' doesn't exist?", event.getName());
66+
}
67+
}
68+
69+
@Override
70+
public void onButtonClick(@NotNull ButtonClickEvent event) {
71+
getArgumentsAndCommandByEvent(event, (command, args) -> command.onButtonClick(event, args));
72+
}
73+
74+
@Override
75+
public void onSelectionMenu(@NotNull SelectionMenuEvent event) {
76+
getArgumentsAndCommandByEvent(event,
77+
(command, args) -> command.onSelectionMenu(event, args));
78+
}
79+
80+
/**
81+
* Gets the arguments as a {@link List} and gets the {@link Command} by the event.
82+
*
83+
* @param event a {@link GenericComponentInteractionCreateEvent}
84+
* @param onSucceed a {@link BiConsumer} that takes the command and the arguments
85+
*/
86+
private void getArgumentsAndCommandByEvent(GenericComponentInteractionCreateEvent event,
87+
BiConsumer<Command, List<String>> onSucceed) {
88+
String[] argsArray = event.getComponentId().split("-");
89+
Command command = commandMap.get(argsArray[0]);
90+
91+
if (command != null) {
92+
List<String> args = new ArrayList<>(Arrays.asList(argsArray));
93+
args = args.subList(2, args.size());
94+
onSucceed.accept(command, args);
95+
}
96+
}
97+
98+
public List<Command> getCommandList() {
99+
return commandList;
100+
}
101+
}

0 commit comments

Comments
 (0)