Skip to content

Commit c1d671c

Browse files
committed
✨ First pass on slash commands checker & responses
1 parent 44ae173 commit c1d671c

File tree

9 files changed

+476
-59
lines changed

9 files changed

+476
-59
lines changed

src/main/java/com/greazi/discordbotfoundation/command/SimpleCommand.java

Lines changed: 28 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import com.greazi.discordbotfoundation.Common;
44
import com.greazi.discordbotfoundation.SimpleBot;
5+
import com.greazi.discordbotfoundation.command.checkers.SlashCommandChecker;
6+
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
7+
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponseProvider;
8+
import com.greazi.discordbotfoundation.command.checkers.response.DefaultCommandProvider;
59
import com.greazi.discordbotfoundation.utils.SimpleEmbedBuilder;
610
import com.greazi.discordbotfoundation.utils.Valid;
711
import com.greazi.discordbotfoundation.utils.expiringmap.ExpiringMap;
@@ -28,7 +32,6 @@
2832
import java.lang.reflect.Method;
2933
import java.nio.channels.Channel;
3034
import java.util.ArrayList;
31-
import java.util.Arrays;
3235
import java.util.List;
3336
import java.util.concurrent.TimeUnit;
3437

@@ -74,6 +77,21 @@ public abstract class SimpleCommand {
7477
@Setter
7578
private String description;
7679

80+
/**
81+
* The command response provider that will be used in
82+
* slash command checkers.
83+
*/
84+
@Getter
85+
@Setter
86+
private SlashCommandResponseProvider responseProvider = new DefaultCommandProvider();
87+
88+
/**
89+
* List of {@link com.greazi.discordbotfoundation.command.checkers.SlashCommandChecker checkers} that will be used
90+
* to check if the command can be executed.
91+
*/
92+
@Getter
93+
private final List<SlashCommandChecker> checkers = new ArrayList<>();
94+
7795
/**
7896
* The permissions that are required to run this command
7997
*/
@@ -139,36 +157,12 @@ public abstract class SimpleCommand {
139157
@Getter @Setter
140158
private List<User> disabledUsers = null;
141159

142-
/**
143-
* Roles that can't use this command
144-
*/
145-
@Getter @Setter
146-
private List<Role> disabledRoles = null;
147-
148-
/**
149-
* Channels that aren't allowed to use this command
150-
*/
151-
@Getter @Setter
152-
private List<Channel> disabledChannels = null;
153-
154160
/**
155161
* Users that are allowed use this command
156162
*/
157163
@Getter @Setter
158164
private List<User> allowedUsers = null;
159165

160-
/**
161-
* Roles that are allowed use this command
162-
*/
163-
@Getter @Setter
164-
private List<Role> allowedRoles = null;
165-
166-
/**
167-
* Channels where this command is allowed
168-
*/
169-
@Getter @Setter
170-
private List<Channel> allowedChannels = null;
171-
172166
/**
173167
* A list of all commands that need to be registered
174168
*/
@@ -298,6 +292,15 @@ public final boolean isCoreCommand() {
298292

299293
protected void execute(@NotNull final SlashCommandInteractionEvent event, final SimpleCommand simpleCommand) {
300294
Common.log("Received command: " + this.getCommand() + " User: " + event.getUser().getName());
295+
// First we run the checkers
296+
for (final SlashCommandChecker checker : checkers) {
297+
final SlashCommandResponse response = checker.check(this, event);
298+
if (!response.isSuccessful()) {
299+
response.respond(event);
300+
return;
301+
}
302+
}
303+
301304
// Set the event variable
302305
simpleCommand.event = event;
303306

@@ -328,45 +331,11 @@ protected void execute(@NotNull final SlashCommandInteractionEvent event, final
328331
canExecute = false;
329332
}
330333

331-
// Check if the user is in the disabled roles list
332-
if (simpleCommand.disabledRoles != null && simpleCommand.member != null) {
333-
for (final Role role : simpleCommand.disabledRoles) {
334-
if (simpleCommand.member.getRoles().contains(role)) {
335-
canExecute = false;
336-
break;
337-
}
338-
}
339-
}
340-
341-
// Check if the channel is in the disabled channels list
342-
if (simpleCommand.disabledChannels != null && simpleCommand.channel != null) {
343-
if (simpleCommand.disabledChannels.contains(simpleCommand.channel) || simpleCommand.disabledChannels.contains(simpleCommand.threadChannel)) {
344-
canExecute = false;
345-
}
346-
}
347-
348334
// Check if the user is in the allowed users list
349335
if (simpleCommand.allowedUsers != null && !simpleCommand.allowedUsers.contains(simpleCommand.user)) {
350336
canExecute = false;
351337
}
352338

353-
// Check if the user is in the allowed roles list
354-
if (simpleCommand.allowedRoles != null && simpleCommand.member != null) {
355-
for (final Role role : simpleCommand.allowedRoles) {
356-
if (simpleCommand.member.getRoles().contains(role)) {
357-
canExecute = true;
358-
break;
359-
}
360-
}
361-
}
362-
363-
// Check if the channel is in the allowed channels list
364-
if (simpleCommand.allowedChannels != null && simpleCommand.channel != null) {
365-
if (!simpleCommand.allowedChannels.contains(simpleCommand.channel) || !simpleCommand.allowedChannels.contains(simpleCommand.threadChannel)) {
366-
canExecute = false;
367-
}
368-
}
369-
370339
// Check if the user has the permission to bypass the cooldown
371340
if (simpleCommand.cooldownBypassPermission != null && simpleCommand.member != null) {
372341
for (final Permission permission : this.cooldownBypassPermission) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.greazi.discordbotfoundation.command.checkers;
2+
3+
import com.greazi.discordbotfoundation.command.SimpleCommand;
4+
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
5+
import net.dv8tion.jda.api.entities.Role;
6+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
7+
import net.dv8tion.jda.internal.utils.Checks;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
/**
14+
* Check if the channel is allowed/disallowed to execute the command.
15+
*/
16+
public class ChannelsChecker implements SlashCommandChecker {
17+
18+
//region Public API
19+
20+
/**
21+
* Create a new channels checker that allows only the specified channels to execute the command.
22+
* @param allowedChannelsIds The list of roles ids that are allowed to execute the command.
23+
* @return The roles checker instance.
24+
*/
25+
public static ChannelsChecker createAllowed(@NotNull List<String> allowedChannelsIds) {
26+
return create(allowedChannelsIds, new ArrayList<>());
27+
}
28+
29+
/**
30+
* Create a new channels checker that disallows the specified channels to execute the command.
31+
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
32+
* @return The roles checker instance.
33+
*/
34+
public static ChannelsChecker createDisallowed(@NotNull List<String> disallowedRolesIds) {
35+
return create(new ArrayList<>(), disallowedRolesIds);
36+
}
37+
38+
/**
39+
* Create a new channels checker that allows and disallows the specified channels to execute the command.
40+
* @param allowedChannelsIds The list of roles ids that are allowed to execute the command.
41+
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
42+
* @return The roles checker instance.
43+
*/
44+
public static ChannelsChecker create(@NotNull List<String> allowedChannelsIds,
45+
@NotNull List<String> disallowedRolesIds) {
46+
Checks.notEmpty(allowedChannelsIds, "allowedChannelsIds");
47+
Checks.noneNull(allowedChannelsIds, "allowedChannelsIds");
48+
Checks.notEmpty(disallowedRolesIds, "disallowedRolesIds");
49+
Checks.noneNull(disallowedRolesIds, "disallowedRolesIds");
50+
51+
return new ChannelsChecker(allowedChannelsIds, disallowedRolesIds);
52+
}
53+
54+
//endregion
55+
56+
private final List<String> allowedChannelsIds;
57+
private final List<String> disallowedRolesIds;
58+
private ChannelsChecker(List<String> allowedChannelsIds, List<String> disallowedRolesIds) {
59+
this.allowedChannelsIds = allowedChannelsIds;
60+
this.disallowedRolesIds = disallowedRolesIds;
61+
}
62+
63+
@NotNull
64+
@Override
65+
public SlashCommandResponse check(@NotNull SimpleCommand command,
66+
@NotNull SlashCommandInteractionEvent context) {
67+
final var provider = command.getResponseProvider();
68+
if (context.getMember() == null) {
69+
return provider.error("You must be in a guild to use this command.");
70+
}
71+
72+
if (allowedChannelsIds.isEmpty() && disallowedRolesIds.isEmpty())
73+
return provider.success();
74+
75+
final var channelId = context.getChannel().getId();
76+
if (!allowedChannelsIds.isEmpty() && !allowedChannelsIds.contains(channelId))
77+
return provider.error("You are not in the allowed channel to use this command.");
78+
79+
if (!disallowedRolesIds.isEmpty() && disallowedRolesIds.contains(channelId))
80+
return provider.error("You are in a disallowed channel to use this command.");
81+
82+
return provider.success();
83+
}
84+
85+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.greazi.discordbotfoundation.command.checkers;
2+
3+
import com.greazi.discordbotfoundation.command.SimpleCommand;
4+
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
5+
import net.dv8tion.jda.api.entities.Role;
6+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
7+
import net.dv8tion.jda.internal.utils.Checks;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
/**
14+
* Check if the member has the required roles to execute the command.
15+
*/
16+
public class RolesChecker implements SlashCommandChecker {
17+
18+
//region Public API
19+
20+
/**
21+
* Create a new roles checker that allows only the specified roles to execute the command.
22+
* @param allowedRolesIds The list of roles ids that are allowed to execute the command.
23+
* @return The roles checker instance.
24+
*/
25+
public static RolesChecker createAllowed(@NotNull List<String> allowedRolesIds) {
26+
return create(allowedRolesIds, new ArrayList<>());
27+
}
28+
29+
/**
30+
* Create a new roles checker that disallows the specified roles to execute the command.
31+
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
32+
* @return The roles checker instance.
33+
*/
34+
public static RolesChecker createDisallowed(@NotNull List<String> disallowedRolesIds) {
35+
return create(new ArrayList<>(), disallowedRolesIds);
36+
}
37+
38+
/**
39+
* Create a new roles checker that allows and disallows the specified roles to execute the command.
40+
* @param allowedRolesIds The list of roles ids that are allowed to execute the command.
41+
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
42+
* @return The roles checker instance.
43+
*/
44+
public static RolesChecker create(@NotNull List<String> allowedRolesIds,
45+
@NotNull List<String> disallowedRolesIds) {
46+
Checks.notEmpty(allowedRolesIds, "allowedRolesIds");
47+
Checks.noneNull(allowedRolesIds, "allowedRolesIds");
48+
Checks.notEmpty(disallowedRolesIds, "disallowedRolesIds");
49+
Checks.noneNull(disallowedRolesIds, "disallowedRolesIds");
50+
51+
return new RolesChecker(allowedRolesIds, disallowedRolesIds);
52+
}
53+
54+
//endregion
55+
56+
private final List<String> allowedRolesIds;
57+
private final List<String> disallowedRolesIds;
58+
private RolesChecker(List<String> allowedRolesIds, List<String> disallowedRolesIds) {
59+
this.allowedRolesIds = allowedRolesIds;
60+
this.disallowedRolesIds = disallowedRolesIds;
61+
}
62+
63+
@NotNull
64+
@Override
65+
public SlashCommandResponse check(@NotNull SimpleCommand command,
66+
@NotNull SlashCommandInteractionEvent context) {
67+
final var provider = command.getResponseProvider();
68+
if (context.getMember() == null) {
69+
return provider.error("You must be in a guild to use this command.");
70+
}
71+
72+
final var memberRoles = context.getMember().getRoles();
73+
for (Role role : memberRoles) {
74+
if (disallowedRolesIds.contains(role.getId())) {
75+
return provider.error("You have a disallowed role to use this command.");
76+
}
77+
}
78+
79+
for (String roleId : allowedRolesIds) {
80+
if (memberRoles.stream().noneMatch(role -> role.getId().equals(roleId))) {
81+
return provider.error("You don't have the required roles to use this command.");
82+
}
83+
}
84+
85+
return provider.success();
86+
}
87+
88+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.greazi.discordbotfoundation.command.checkers;
2+
3+
import com.greazi.discordbotfoundation.command.SimpleCommand;
4+
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
5+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
/**
9+
* Interface for checking if a slash command context is valid to execute the actual command's code.
10+
*/
11+
public interface SlashCommandChecker {
12+
13+
@NotNull
14+
SlashCommandResponse check(@NotNull SimpleCommand command,
15+
@NotNull SlashCommandInteractionEvent context);
16+
17+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.greazi.discordbotfoundation.command.checkers.response;
2+
3+
import org.jetbrains.annotations.Nullable;
4+
5+
/**
6+
* Default {@link SlashCommandResponseProvider command provider} implementation,
7+
* using {@link com.greazi.discordbotfoundation.command.checkers.response.EmbedResponse embed response} as default responses.
8+
*/
9+
public class DefaultCommandProvider implements SlashCommandResponseProvider {
10+
11+
@Override
12+
public SlashCommandResponse success(@Nullable String message) {
13+
return EmbedResponse.success("Success", message != null ? message : "The command was executed successfully.");
14+
}
15+
16+
@Override
17+
public SlashCommandResponse error(@Nullable String message) {
18+
return EmbedResponse.error("Error!", message != null ? message : "An error occurred while executing the command.");
19+
}
20+
21+
@Override
22+
public SlashCommandResponse warning(@Nullable String message) {
23+
return EmbedResponse.warning("Warning!", message != null ? message : "A warning occurred while executing the command.");
24+
}
25+
26+
@Override
27+
public SlashCommandResponse info(@Nullable String message) {
28+
return EmbedResponse.info("Information", message != null ? message : "An info occurred while executing the command.");
29+
}
30+
}

0 commit comments

Comments
 (0)