Skip to content

Commit a0fb71e

Browse files
committed
Copy paste of old command rework from #382
1 parent 2fa1b8f commit a0fb71e

37 files changed

+506
-198
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.togetherjava.tjbot.commands;
2+
3+
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
4+
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
5+
import net.dv8tion.jda.api.interactions.commands.Command;
6+
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.togetherjava.tjbot.commands.componentids.ComponentId;
9+
import org.togetherjava.tjbot.commands.componentids.ComponentIdGenerator;
10+
import org.togetherjava.tjbot.commands.componentids.Lifespan;
11+
12+
import java.util.List;
13+
14+
/**
15+
* Represents a Discord command.
16+
* <p>
17+
* All commands have to implement this interface. For convenience, there is a
18+
* {@link BotCommandAdapter} available that implemented most methods already. A new command can then
19+
* be registered by adding it to {@link Features}.
20+
* <p>
21+
* <p>
22+
* Commands can either be visible globally in Discord or just to specific guilds. Some
23+
* configurations can be made via {@link CommandData}, which is then to be returned by
24+
* {@link #getData()} where the system will then pick it up from.
25+
* <p>
26+
* After registration, the system will notify a command whenever one of its corresponding command
27+
* method, buttons ({@link #onButtonClick(ButtonInteractionEvent, List)}) or menus
28+
* ({@link #onSelectionMenu(SelectMenuInteractionEvent, List)}) have been triggered.
29+
* <p>
30+
* <p>
31+
* Some example commands are available in {@link org.togetherjava.tjbot.commands.basic}.
32+
*/
33+
public interface BotCommand extends UserInteractor {
34+
35+
/**
36+
* Gets the type of this command.
37+
* <p>
38+
* After registration of the command, the type must not change anymore.
39+
*
40+
* @return the type of the command
41+
*/
42+
@NotNull
43+
Command.Type getType();
44+
45+
46+
/**
47+
* Gets the visibility of this command.
48+
* <p>
49+
* After registration of the command, the visibility must not change anymore.
50+
*
51+
* @return the visibility of the command
52+
*/
53+
@NotNull
54+
CommandVisibility getVisibility();
55+
56+
/**
57+
* Gets the command data belonging to this command.
58+
* <p>
59+
* See {@link net.dv8tion.jda.api.interactions.commands.build.Commands Commands} for details on how to
60+
* create and configure instances of it.
61+
* <p>
62+
* <p>
63+
* This method may be called multiple times, implementations must not create new data each time
64+
* but instead configure it once beforehand. The core system will automatically call this method
65+
* to register the command to Discord.
66+
*
67+
* @return the command data of this command
68+
*/
69+
@NotNull
70+
CommandData getData();
71+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package org.togetherjava.tjbot.commands;
2+
3+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
4+
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
5+
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
6+
import net.dv8tion.jda.api.interactions.commands.Command;
7+
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
8+
import org.jetbrains.annotations.Contract;
9+
import org.jetbrains.annotations.NotNull;
10+
import org.togetherjava.tjbot.commands.componentids.ComponentId;
11+
import org.togetherjava.tjbot.commands.componentids.ComponentIdGenerator;
12+
import org.togetherjava.tjbot.commands.componentids.Lifespan;
13+
14+
import java.util.Arrays;
15+
import java.util.List;
16+
import java.util.Objects;
17+
18+
/**
19+
* Adapter implementation of a {@link BotCommand}. The minimal setup only requires implementation of
20+
* their respective command method. A new command can then be registered by adding it to
21+
* {@link Features}.
22+
* <p>
23+
* Further, {@link #onButtonClick(ButtonInteractionEvent, List)} and
24+
* {@link #onSelectionMenu(SelectMenuInteractionEvent, List)} can be overridden if desired. The
25+
* default implementation is empty, the adapter will not react to such events.
26+
* <p>
27+
* <p>
28+
* The adapter manages some getters for you, you've to create the {@link CommandData} yourself. See
29+
* {@link #BotCommandAdapter(CommandData, CommandVisibility)}} for more info on that. Minimal
30+
* modifications can be done on the {@link CommandData} returned by {@link #getData()}.
31+
* <p>
32+
* <p>
33+
* If implementations want to add buttons or selection menus, it is highly advised to use component
34+
* IDs generated by {@link #generateComponentId(String...)}, which will automatically create IDs
35+
* that are valid per {@link SlashCommand#onSlashCommand(SlashCommandInteractionEvent)}.
36+
* <p>
37+
* <p>
38+
* Some example commands are available in {@link org.togetherjava.tjbot.commands.basic}.
39+
* Registration of commands can be done in {@link Features}.
40+
*/
41+
public abstract class BotCommandAdapter implements BotCommand {
42+
private final String name;
43+
private final Command.Type type;
44+
private final CommandVisibility visibility;
45+
private final CommandData data;
46+
private ComponentIdGenerator componentIdGenerator;
47+
48+
/**
49+
* Creates a new adapter with the given data.
50+
*
51+
* @param data the data for this command
52+
* @param visibility the visibility of the command
53+
*/
54+
protected BotCommandAdapter(@NotNull CommandData data, @NotNull CommandVisibility visibility) {
55+
this.data = Objects.requireNonNull(data, "The data shouldn't be null");
56+
this.visibility = Objects.requireNonNull(visibility, "The visibility shouldn't be null");
57+
58+
this.name = data.getName();
59+
this.type = data.getType();
60+
}
61+
62+
@Override
63+
public final @NotNull String getName() {
64+
return name;
65+
}
66+
67+
@NotNull
68+
@Override
69+
public Command.Type getType() {
70+
return type;
71+
}
72+
73+
@Override
74+
public final @NotNull CommandVisibility getVisibility() {
75+
return visibility;
76+
}
77+
78+
@Override
79+
public @NotNull CommandData getData() {
80+
return data;
81+
}
82+
83+
@Override
84+
@Contract(mutates = "this")
85+
public final void acceptComponentIdGenerator(@NotNull ComponentIdGenerator generator) {
86+
componentIdGenerator =
87+
Objects.requireNonNull(generator, "The given generator cannot be null");
88+
}
89+
90+
@SuppressWarnings("NoopMethodInAbstractClass")
91+
@Override
92+
public void onButtonClick(@NotNull ButtonInteractionEvent event, @NotNull List<String> args) {
93+
// Adapter does not react by default, subclasses may change this behavior
94+
}
95+
96+
@SuppressWarnings("NoopMethodInAbstractClass")
97+
@Override
98+
public void onSelectionMenu(@NotNull SelectMenuInteractionEvent event,
99+
@NotNull List<String> args) {
100+
// Adapter does not react by default, subclasses may change this behavior
101+
}
102+
103+
/**
104+
* Helper method to generate component IDs that are considered valid per
105+
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)}.
106+
* <p>
107+
* They can be used to create buttons or selection menus and transport additional data
108+
* throughout the event (e.g. the user id who created the button dialog).
109+
* <p>
110+
* IDs generated by this method have a regular lifespan, meaning that they might get evicted and
111+
* expire after not being used for a long time. Use
112+
* {@link #generateComponentId(Lifespan, String...)} to set other lifespans, if desired.
113+
*
114+
* @param args the extra arguments that should be part of the ID
115+
* @return the generated component ID
116+
*/
117+
@SuppressWarnings("OverloadedVarargsMethod")
118+
protected final @NotNull String generateComponentId(@NotNull String... args) {
119+
return generateComponentId(Lifespan.REGULAR, args);
120+
}
121+
122+
/**
123+
* Helper method to generate component IDs that are considered valid per
124+
* {@link #acceptComponentIdGenerator(ComponentIdGenerator)}.
125+
* <p>
126+
* They can be used to create buttons or selection menus and transport additional data
127+
* throughout the event (e.g. the user id who created the button dialog).
128+
*
129+
* @param lifespan the lifespan of the component id, controls when an id that was not used for a
130+
* long time might be evicted and expire
131+
* @param args the extra arguments that should be part of the ID
132+
* @return the generated component ID
133+
*/
134+
@SuppressWarnings({"OverloadedVarargsMethod", "WeakerAccess"})
135+
protected final @NotNull String generateComponentId(@NotNull Lifespan lifespan,
136+
@NotNull String... args) {
137+
return componentIdGenerator.generate(new ComponentId(getName(), Arrays.asList(args)),
138+
lifespan);
139+
}
140+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* Visibility of a slash command, i.e. in which context it can be used by users.
55
*/
6-
public enum SlashCommandVisibility {
6+
public enum CommandVisibility {
77
/**
88
* The command can be used within the context of a guild.
99
*/
@@ -12,4 +12,4 @@ public enum SlashCommandVisibility {
1212
* The command can be used globally, outside a guild context.
1313
*/
1414
GLOBAL
15-
}
15+
}

application/src/main/java/org/togetherjava/tjbot/commands/Features.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
9797
features.add(new RejoinModerationRoleListener(actionsStore, config));
9898
features.add(new OnGuildLeaveCloseThreadListener(database));
9999

100+
// Message context commands
101+
102+
// User context commands
103+
100104
// Slash commands
101105
features.add(new LogLevelCommand());
102106
features.add(new PingCommand());
@@ -130,4 +134,4 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
130134

131135
return features;
132136
}
133-
}
137+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.togetherjava.tjbot.commands;
2+
3+
import net.dv8tion.jda.api.entities.Emoji;
4+
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
5+
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
6+
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
7+
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
8+
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
9+
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.togetherjava.tjbot.commands.componentids.ComponentIdGenerator;
12+
13+
import java.util.List;
14+
15+
/**
16+
* A message context-command is a command, accessible when right-clicking on a message.
17+
*
18+
* <p>
19+
* Represents a Discord message context-command. Mostly decorating
20+
* {@link net.dv8tion.jda.api.interactions.commands.Command}.
21+
* <p>
22+
* All message context-commands have to implement this interface. For convenience, there is a
23+
* {@link BotCommandAdapter} available that implemented most methods already. A new command can then
24+
* be registered by adding it to {@link Features}.
25+
* <p>
26+
* <p>
27+
* Context commands can either be visible globally in Discord or just to specific guilds. Minor
28+
* adjustments can be made via {@link CommandData}, which is then to be returned by
29+
* {@link #getData()} where the system will then pick it up from.
30+
* <p>
31+
* After registration, the system will notify a command whenever one of its corresponding message
32+
* context-commands ({@link #onMessageContext(MessageContextInteractionEvent)}), buttons
33+
* ({@link #onButtonClick(ButtonInteractionEvent, List)}) or menus
34+
* ({@link #onSelectionMenu(SelectMenuInteractionEvent, List)}) have been triggered.
35+
* <p>
36+
* <p>
37+
* Some example commands are available in {@link org.togetherjava.tjbot.commands.basic}.
38+
*/
39+
public interface MessageContextCommand extends BotCommand {
40+
41+
/**
42+
* Triggered by the core system when a message context-command corresponding to this
43+
* implementation (based on {@link #getData()}) has been triggered.
44+
* <p>
45+
* This method may be called multithreaded. In particular, there are no guarantees that it will
46+
* be executed on the same thread repeatedly or on the same thread that other event methods have
47+
* been called on.
48+
* <p>
49+
* Details are available in the given event and the event also enables implementations to
50+
* respond to it.
51+
* <p>
52+
* Buttons or menus have to be created with a component ID (see
53+
* {@link ComponentInteraction#getComponentId()},
54+
* {@link net.dv8tion.jda.api.interactions.components.buttons.Button#of(ButtonStyle, String, Emoji)})
55+
* in a very specific format, otherwise the core system will fail to identify the command that
56+
* corresponded to the button or menu click event and is unable to route it back.
57+
* <p>
58+
* See {@link #acceptComponentIdGenerator(ComponentIdGenerator)} for more info.
59+
*
60+
* @param event the event that triggered this
61+
*/
62+
void onMessageContext(@NotNull MessageContextInteractionEvent event);
63+
}

0 commit comments

Comments
 (0)