Skip to content

Commit

Permalink
✨ First pass on slash commands checker & responses
Browse files Browse the repository at this point in the history
  • Loading branch information
ItsTheSky committed Apr 26, 2024
1 parent 44ae173 commit c1d671c
Show file tree
Hide file tree
Showing 9 changed files with 476 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import com.greazi.discordbotfoundation.Common;
import com.greazi.discordbotfoundation.SimpleBot;
import com.greazi.discordbotfoundation.command.checkers.SlashCommandChecker;
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponseProvider;
import com.greazi.discordbotfoundation.command.checkers.response.DefaultCommandProvider;
import com.greazi.discordbotfoundation.utils.SimpleEmbedBuilder;
import com.greazi.discordbotfoundation.utils.Valid;
import com.greazi.discordbotfoundation.utils.expiringmap.ExpiringMap;
Expand All @@ -28,7 +32,6 @@
import java.lang.reflect.Method;
import java.nio.channels.Channel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -74,6 +77,21 @@ public abstract class SimpleCommand {
@Setter
private String description;

/**
* The command response provider that will be used in
* slash command checkers.
*/
@Getter
@Setter
private SlashCommandResponseProvider responseProvider = new DefaultCommandProvider();

/**
* List of {@link com.greazi.discordbotfoundation.command.checkers.SlashCommandChecker checkers} that will be used
* to check if the command can be executed.
*/
@Getter
private final List<SlashCommandChecker> checkers = new ArrayList<>();

/**
* The permissions that are required to run this command
*/
Expand Down Expand Up @@ -139,36 +157,12 @@ public abstract class SimpleCommand {
@Getter @Setter
private List<User> disabledUsers = null;

/**
* Roles that can't use this command
*/
@Getter @Setter
private List<Role> disabledRoles = null;

/**
* Channels that aren't allowed to use this command
*/
@Getter @Setter
private List<Channel> disabledChannels = null;

/**
* Users that are allowed use this command
*/
@Getter @Setter
private List<User> allowedUsers = null;

/**
* Roles that are allowed use this command
*/
@Getter @Setter
private List<Role> allowedRoles = null;

/**
* Channels where this command is allowed
*/
@Getter @Setter
private List<Channel> allowedChannels = null;

/**
* A list of all commands that need to be registered
*/
Expand Down Expand Up @@ -298,6 +292,15 @@ public final boolean isCoreCommand() {

protected void execute(@NotNull final SlashCommandInteractionEvent event, final SimpleCommand simpleCommand) {
Common.log("Received command: " + this.getCommand() + " User: " + event.getUser().getName());
// First we run the checkers
for (final SlashCommandChecker checker : checkers) {
final SlashCommandResponse response = checker.check(this, event);
if (!response.isSuccessful()) {
response.respond(event);
return;
}
}

// Set the event variable
simpleCommand.event = event;

Expand Down Expand Up @@ -328,45 +331,11 @@ protected void execute(@NotNull final SlashCommandInteractionEvent event, final
canExecute = false;
}

// Check if the user is in the disabled roles list
if (simpleCommand.disabledRoles != null && simpleCommand.member != null) {
for (final Role role : simpleCommand.disabledRoles) {
if (simpleCommand.member.getRoles().contains(role)) {
canExecute = false;
break;
}
}
}

// Check if the channel is in the disabled channels list
if (simpleCommand.disabledChannels != null && simpleCommand.channel != null) {
if (simpleCommand.disabledChannels.contains(simpleCommand.channel) || simpleCommand.disabledChannels.contains(simpleCommand.threadChannel)) {
canExecute = false;
}
}

// Check if the user is in the allowed users list
if (simpleCommand.allowedUsers != null && !simpleCommand.allowedUsers.contains(simpleCommand.user)) {
canExecute = false;
}

// Check if the user is in the allowed roles list
if (simpleCommand.allowedRoles != null && simpleCommand.member != null) {
for (final Role role : simpleCommand.allowedRoles) {
if (simpleCommand.member.getRoles().contains(role)) {
canExecute = true;
break;
}
}
}

// Check if the channel is in the allowed channels list
if (simpleCommand.allowedChannels != null && simpleCommand.channel != null) {
if (!simpleCommand.allowedChannels.contains(simpleCommand.channel) || !simpleCommand.allowedChannels.contains(simpleCommand.threadChannel)) {
canExecute = false;
}
}

// Check if the user has the permission to bypass the cooldown
if (simpleCommand.cooldownBypassPermission != null && simpleCommand.member != null) {
for (final Permission permission : this.cooldownBypassPermission) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.greazi.discordbotfoundation.command.checkers;

import com.greazi.discordbotfoundation.command.SimpleCommand;
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.internal.utils.Checks;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
* Check if the channel is allowed/disallowed to execute the command.
*/
public class ChannelsChecker implements SlashCommandChecker {

//region Public API

/**
* Create a new channels checker that allows only the specified channels to execute the command.
* @param allowedChannelsIds The list of roles ids that are allowed to execute the command.
* @return The roles checker instance.
*/
public static ChannelsChecker createAllowed(@NotNull List<String> allowedChannelsIds) {
return create(allowedChannelsIds, new ArrayList<>());
}

/**
* Create a new channels checker that disallows the specified channels to execute the command.
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
* @return The roles checker instance.
*/
public static ChannelsChecker createDisallowed(@NotNull List<String> disallowedRolesIds) {
return create(new ArrayList<>(), disallowedRolesIds);
}

/**
* Create a new channels checker that allows and disallows the specified channels to execute the command.
* @param allowedChannelsIds The list of roles ids that are allowed to execute the command.
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
* @return The roles checker instance.
*/
public static ChannelsChecker create(@NotNull List<String> allowedChannelsIds,
@NotNull List<String> disallowedRolesIds) {
Checks.notEmpty(allowedChannelsIds, "allowedChannelsIds");
Checks.noneNull(allowedChannelsIds, "allowedChannelsIds");
Checks.notEmpty(disallowedRolesIds, "disallowedRolesIds");
Checks.noneNull(disallowedRolesIds, "disallowedRolesIds");

return new ChannelsChecker(allowedChannelsIds, disallowedRolesIds);
}

//endregion

private final List<String> allowedChannelsIds;
private final List<String> disallowedRolesIds;
private ChannelsChecker(List<String> allowedChannelsIds, List<String> disallowedRolesIds) {
this.allowedChannelsIds = allowedChannelsIds;
this.disallowedRolesIds = disallowedRolesIds;
}

@NotNull
@Override
public SlashCommandResponse check(@NotNull SimpleCommand command,
@NotNull SlashCommandInteractionEvent context) {
final var provider = command.getResponseProvider();
if (context.getMember() == null) {
return provider.error("You must be in a guild to use this command.");
}

if (allowedChannelsIds.isEmpty() && disallowedRolesIds.isEmpty())
return provider.success();

final var channelId = context.getChannel().getId();
if (!allowedChannelsIds.isEmpty() && !allowedChannelsIds.contains(channelId))
return provider.error("You are not in the allowed channel to use this command.");

if (!disallowedRolesIds.isEmpty() && disallowedRolesIds.contains(channelId))
return provider.error("You are in a disallowed channel to use this command.");

return provider.success();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.greazi.discordbotfoundation.command.checkers;

import com.greazi.discordbotfoundation.command.SimpleCommand;
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.internal.utils.Checks;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
* Check if the member has the required roles to execute the command.
*/
public class RolesChecker implements SlashCommandChecker {

//region Public API

/**
* Create a new roles checker that allows only the specified roles to execute the command.
* @param allowedRolesIds The list of roles ids that are allowed to execute the command.
* @return The roles checker instance.
*/
public static RolesChecker createAllowed(@NotNull List<String> allowedRolesIds) {
return create(allowedRolesIds, new ArrayList<>());
}

/**
* Create a new roles checker that disallows the specified roles to execute the command.
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
* @return The roles checker instance.
*/
public static RolesChecker createDisallowed(@NotNull List<String> disallowedRolesIds) {
return create(new ArrayList<>(), disallowedRolesIds);
}

/**
* Create a new roles checker that allows and disallows the specified roles to execute the command.
* @param allowedRolesIds The list of roles ids that are allowed to execute the command.
* @param disallowedRolesIds The list of roles ids that are disallowed to execute the command.
* @return The roles checker instance.
*/
public static RolesChecker create(@NotNull List<String> allowedRolesIds,
@NotNull List<String> disallowedRolesIds) {
Checks.notEmpty(allowedRolesIds, "allowedRolesIds");
Checks.noneNull(allowedRolesIds, "allowedRolesIds");
Checks.notEmpty(disallowedRolesIds, "disallowedRolesIds");
Checks.noneNull(disallowedRolesIds, "disallowedRolesIds");

return new RolesChecker(allowedRolesIds, disallowedRolesIds);
}

//endregion

private final List<String> allowedRolesIds;
private final List<String> disallowedRolesIds;
private RolesChecker(List<String> allowedRolesIds, List<String> disallowedRolesIds) {
this.allowedRolesIds = allowedRolesIds;
this.disallowedRolesIds = disallowedRolesIds;
}

@NotNull
@Override
public SlashCommandResponse check(@NotNull SimpleCommand command,
@NotNull SlashCommandInteractionEvent context) {
final var provider = command.getResponseProvider();
if (context.getMember() == null) {
return provider.error("You must be in a guild to use this command.");
}

final var memberRoles = context.getMember().getRoles();
for (Role role : memberRoles) {
if (disallowedRolesIds.contains(role.getId())) {
return provider.error("You have a disallowed role to use this command.");
}
}

for (String roleId : allowedRolesIds) {
if (memberRoles.stream().noneMatch(role -> role.getId().equals(roleId))) {
return provider.error("You don't have the required roles to use this command.");
}
}

return provider.success();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.greazi.discordbotfoundation.command.checkers;

import com.greazi.discordbotfoundation.command.SimpleCommand;
import com.greazi.discordbotfoundation.command.checkers.response.SlashCommandResponse;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.jetbrains.annotations.NotNull;

/**
* Interface for checking if a slash command context is valid to execute the actual command's code.
*/
public interface SlashCommandChecker {

@NotNull
SlashCommandResponse check(@NotNull SimpleCommand command,
@NotNull SlashCommandInteractionEvent context);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.greazi.discordbotfoundation.command.checkers.response;

import org.jetbrains.annotations.Nullable;

/**
* Default {@link SlashCommandResponseProvider command provider} implementation,
* using {@link com.greazi.discordbotfoundation.command.checkers.response.EmbedResponse embed response} as default responses.
*/
public class DefaultCommandProvider implements SlashCommandResponseProvider {

@Override
public SlashCommandResponse success(@Nullable String message) {
return EmbedResponse.success("Success", message != null ? message : "The command was executed successfully.");
}

@Override
public SlashCommandResponse error(@Nullable String message) {
return EmbedResponse.error("Error!", message != null ? message : "An error occurred while executing the command.");
}

@Override
public SlashCommandResponse warning(@Nullable String message) {
return EmbedResponse.warning("Warning!", message != null ? message : "A warning occurred while executing the command.");
}

@Override
public SlashCommandResponse info(@Nullable String message) {
return EmbedResponse.info("Information", message != null ? message : "An info occurred while executing the command.");
}
}
Loading

0 comments on commit c1d671c

Please sign in to comment.