2024-06-24 12:56:01 +00:00
|
|
|
package cc.fascinated.bat.service;
|
|
|
|
|
2024-06-27 20:05:54 +00:00
|
|
|
import cc.fascinated.bat.Consts;
|
2024-06-28 02:24:55 +00:00
|
|
|
import cc.fascinated.bat.command.*;
|
2024-06-24 12:56:01 +00:00
|
|
|
import cc.fascinated.bat.common.EmbedUtils;
|
2024-06-25 10:55:26 +00:00
|
|
|
import cc.fascinated.bat.model.BatGuild;
|
|
|
|
import cc.fascinated.bat.model.BatUser;
|
2024-06-27 15:01:27 +00:00
|
|
|
import lombok.Getter;
|
2024-06-24 12:56:01 +00:00
|
|
|
import lombok.NonNull;
|
|
|
|
import lombok.extern.log4j.Log4j2;
|
|
|
|
import net.dv8tion.jda.api.JDA;
|
2024-06-25 23:31:16 +00:00
|
|
|
import net.dv8tion.jda.api.Permission;
|
2024-06-24 12:56:01 +00:00
|
|
|
import net.dv8tion.jda.api.entities.Guild;
|
|
|
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
|
|
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
2024-06-27 15:01:27 +00:00
|
|
|
import net.dv8tion.jda.api.interactions.commands.Command;
|
2024-06-24 12:56:01 +00:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.context.annotation.DependsOn;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
2024-06-27 18:36:52 +00:00
|
|
|
import java.util.*;
|
2024-06-24 12:56:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @author Fascinated (fascinated7)
|
|
|
|
*/
|
2024-06-26 15:17:57 +00:00
|
|
|
@Service
|
2024-06-27 18:48:15 +00:00
|
|
|
@Log4j2
|
|
|
|
@Getter
|
2024-06-24 12:56:01 +00:00
|
|
|
@DependsOn("discordService")
|
|
|
|
public class CommandService extends ListenerAdapter {
|
|
|
|
/**
|
|
|
|
* The registered commands
|
|
|
|
*/
|
|
|
|
private final Map<String, BatCommand> commands = new HashMap<>();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The guild service to use
|
|
|
|
*/
|
|
|
|
private final GuildService guildService;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The user service to use
|
|
|
|
*/
|
|
|
|
private final UserService userService;
|
|
|
|
|
|
|
|
@Autowired
|
2024-06-25 16:20:19 +00:00
|
|
|
public CommandService(@NonNull GuildService guildService, @NonNull UserService userService) {
|
2024-06-24 12:56:01 +00:00
|
|
|
this.guildService = guildService;
|
|
|
|
this.userService = userService;
|
|
|
|
DiscordService.JDA.addEventListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a command
|
|
|
|
*
|
|
|
|
* @param command The command to register
|
|
|
|
*/
|
|
|
|
public void registerCommand(@NonNull BatCommand command) {
|
2024-06-27 18:36:52 +00:00
|
|
|
String commandName = command.getCommandInfo().name().toLowerCase();
|
|
|
|
if (commands.get(commandName) != null) {
|
2024-06-25 14:43:36 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-06-27 18:36:52 +00:00
|
|
|
log.info("Registered command \"{}\"", command.getCommandInfo().name());
|
|
|
|
commands.put(commandName, command);
|
2024-06-24 12:56:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers all slash commands
|
|
|
|
*/
|
|
|
|
public void registerSlashCommands() {
|
|
|
|
log.info("Registering all slash commands");
|
|
|
|
JDA jda = DiscordService.JDA;
|
|
|
|
long before = System.currentTimeMillis();
|
|
|
|
|
|
|
|
// Unregister all commands that Discord has but we don't
|
|
|
|
jda.retrieveCommands().complete().forEach(command -> {
|
2024-06-28 02:24:55 +00:00
|
|
|
CommandInfo commandInfo = commands.get(command.getName()).getCommandInfo();
|
|
|
|
if (commands.containsKey(command.getName()) && (commandInfo.category().isHidden() || commandInfo.botOwnerOnly())) {
|
2024-06-27 20:05:54 +00:00
|
|
|
jda.deleteCommandById(command.getId()).complete(); // Unregister the command on Discord
|
2024-06-28 02:24:55 +00:00
|
|
|
log.info("Unregistered hidden command \"{}\" from Discord", command.getName());
|
2024-06-27 20:05:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-06-24 12:56:01 +00:00
|
|
|
if (commands.containsKey(command.getName())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
jda.deleteCommandById(command.getId()).complete(); // Unregister the command on Discord
|
|
|
|
log.info("Unregistered unknown command \"{}\" from Discord", command.getName());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Register all commands
|
2024-06-27 20:05:54 +00:00
|
|
|
List<Command> discordCommands = jda.updateCommands().addCommands(commands.values().stream()
|
2024-06-27 20:12:31 +00:00
|
|
|
.filter(command -> !command.getCategory().isHidden() || !command.isBotOwnerOnly())
|
2024-06-27 20:05:54 +00:00
|
|
|
.map(BatCommand::getCommandData).toList()).complete();
|
2024-06-27 15:01:27 +00:00
|
|
|
for (Command discordCommand : discordCommands) {
|
|
|
|
commands.get(discordCommand.getName()).setCommandSnowflake(discordCommand.getIdLong());
|
2024-06-27 15:24:08 +00:00
|
|
|
if (!discordCommand.getSubcommands().isEmpty()) {
|
|
|
|
for (Command.Subcommand subCommand : discordCommand.getSubcommands()) {
|
|
|
|
commands.get(discordCommand.getName()).getSubCommands().get(subCommand.getName()).setCommandSnowflake(subCommand.getIdLong());
|
|
|
|
}
|
|
|
|
}
|
2024-06-27 15:01:27 +00:00
|
|
|
}
|
2024-06-27 20:05:54 +00:00
|
|
|
|
2024-06-27 20:25:06 +00:00
|
|
|
Guild adminGuild = jda.getGuildById(Consts.ADMIN_GUILD);
|
|
|
|
if (adminGuild != null) {
|
|
|
|
adminGuild.updateCommands().addCommands(commands.values().stream()
|
|
|
|
.filter(command -> command.getCategory().isHidden() || command.isBotOwnerOnly())
|
|
|
|
.map(BatCommand::getCommandData).toList()).complete();
|
|
|
|
} else {
|
|
|
|
log.error("Unable to find the admin guild to register hidden commands");
|
|
|
|
}
|
2024-06-27 20:05:54 +00:00
|
|
|
|
2024-06-24 12:56:01 +00:00
|
|
|
log.info("Registered all slash commands in {}ms", System.currentTimeMillis() - before);
|
|
|
|
}
|
|
|
|
|
2024-06-27 15:30:47 +00:00
|
|
|
/**
|
|
|
|
* Gets commands that are in a specific category
|
|
|
|
*
|
|
|
|
* @param category The category
|
|
|
|
* @return The commands
|
|
|
|
*/
|
2024-06-27 20:05:54 +00:00
|
|
|
public List<BatCommand> getCommandsByCategory(Category category, boolean hideHiddenCategories) {
|
2024-06-27 20:21:56 +00:00
|
|
|
return commands.values().stream().filter(command -> command.getCategory() == category && (hideHiddenCategories && !category.isHidden())).toList();
|
2024-06-27 15:30:47 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 12:56:01 +00:00
|
|
|
@Override
|
|
|
|
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
|
|
|
|
Guild discordGuild = event.getGuild();
|
2024-06-25 23:41:47 +00:00
|
|
|
if (event.getUser().isBot()) {
|
2024-06-24 12:56:01 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
String commandName = event.getName();
|
|
|
|
BatCommand command = commands.get(commandName);
|
|
|
|
if (command == null) {
|
|
|
|
return;
|
|
|
|
}
|
2024-06-26 19:41:31 +00:00
|
|
|
boolean ranInsideGuild = discordGuild != null;
|
|
|
|
BatGuild guild = ranInsideGuild ? guildService.getGuild(discordGuild.getId()) : null;
|
2024-06-24 12:56:01 +00:00
|
|
|
BatUser user = userService.getUser(event.getUser().getId());
|
2024-06-25 23:31:16 +00:00
|
|
|
|
2024-06-27 20:05:54 +00:00
|
|
|
if (command.getCommandInfo().botOwnerOnly() && !user.getId().equalsIgnoreCase(Consts.BOT_OWNER)) {
|
|
|
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
|
|
|
.setDescription("You do not have permission to execute this command")
|
|
|
|
.build()).setEphemeral(true).queue();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-24 12:56:01 +00:00
|
|
|
try {
|
2024-06-25 23:31:16 +00:00
|
|
|
BatCommandExecutor executor = null;
|
2024-06-28 18:11:37 +00:00
|
|
|
CommandInfo commandInfo = command.getCommandInfo();
|
2024-06-25 23:31:16 +00:00
|
|
|
List<Permission> requiredPermissions = new ArrayList<>();
|
2024-06-28 18:07:01 +00:00
|
|
|
boolean isSubCommand = false;
|
2024-06-25 23:31:16 +00:00
|
|
|
|
2024-06-25 10:14:12 +00:00
|
|
|
// No args provided, use the main command executor
|
2024-06-24 16:42:57 +00:00
|
|
|
if (event.getInteraction().getSubcommandName() == null) {
|
2024-06-25 23:31:16 +00:00
|
|
|
executor = command;
|
2024-06-27 18:36:52 +00:00
|
|
|
requiredPermissions.addAll(Arrays.asList(command.getCommandInfo().requiredPermissions()));
|
2024-06-25 23:31:16 +00:00
|
|
|
} else {
|
|
|
|
// Subcommand provided, use the subcommand executor
|
|
|
|
for (Map.Entry<String, BatSubCommand> subCommand : command.getSubCommands().entrySet()) {
|
|
|
|
if (subCommand.getKey().equalsIgnoreCase(event.getInteraction().getSubcommandName())) {
|
|
|
|
executor = subCommand.getValue();
|
2024-06-27 18:36:52 +00:00
|
|
|
requiredPermissions.addAll(Arrays.asList(subCommand.getValue().getCommandInfo().requiredPermissions()));
|
|
|
|
requiredPermissions.addAll(Arrays.asList(command.getCommandInfo().requiredPermissions())); // not sure if we'd want this, but it's here for now
|
2024-06-28 18:07:01 +00:00
|
|
|
isSubCommand = true;
|
2024-06-28 18:11:37 +00:00
|
|
|
commandInfo = subCommand.getValue().getCommandInfo();
|
2024-06-25 23:31:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (executor == null) {
|
|
|
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
|
|
|
.setDescription("Unable to find a command executor the command name ):")
|
|
|
|
.build()).queue();
|
2024-06-25 10:14:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-25 23:31:16 +00:00
|
|
|
// Check if the user has the required permissions
|
2024-06-26 19:41:31 +00:00
|
|
|
if (ranInsideGuild && event.getMember() != null) {
|
2024-06-25 23:31:16 +00:00
|
|
|
List<Permission> missingPermissions = new ArrayList<>();
|
|
|
|
for (Permission permission : requiredPermissions) {
|
|
|
|
if (!event.getMember().hasPermission(permission)) {
|
|
|
|
missingPermissions.add(permission);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!missingPermissions.isEmpty()) {
|
2024-06-27 18:48:15 +00:00
|
|
|
StringBuilder missing = new StringBuilder();
|
|
|
|
for (Permission permission : missingPermissions) {
|
|
|
|
missing.append("`").append(permission.getName()).append("`").append(", ");
|
|
|
|
}
|
2024-06-25 23:31:16 +00:00
|
|
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
2024-06-27 18:48:15 +00:00
|
|
|
.setDescription("You are missing the following permissions to execute this command:\n" +
|
|
|
|
missing.substring(0, missing.length() - 2))
|
2024-06-26 15:17:57 +00:00
|
|
|
.build())
|
|
|
|
.setEphemeral(true)
|
|
|
|
.queue();
|
2024-06-25 23:31:16 +00:00
|
|
|
return;
|
2024-06-24 12:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-28 18:07:01 +00:00
|
|
|
|
2024-06-28 18:11:37 +00:00
|
|
|
if (isSubCommand && commandInfo.guildOnly() && !ranInsideGuild) {
|
2024-06-28 18:07:01 +00:00
|
|
|
event.replyEmbeds(EmbedUtils.errorEmbed()
|
|
|
|
.setDescription("This command can only be executed in a guild")
|
|
|
|
.build()).setEphemeral(true).queue();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-27 12:36:03 +00:00
|
|
|
log.info("Executing command \"{}\" for user \"{}\"", commandName, user.getDiscordUser().getName());
|
2024-06-26 19:41:31 +00:00
|
|
|
executor.execute(guild, user, ranInsideGuild ? event.getChannel().asTextChannel() : event.getChannel().asPrivateChannel(),
|
2024-06-25 23:41:47 +00:00
|
|
|
event.getMember(), event.getInteraction());
|
2024-06-24 12:56:01 +00:00
|
|
|
} catch (Exception ex) {
|
|
|
|
log.error("An error occurred while executing command \"{}\"", commandName, ex);
|
2024-06-25 12:55:54 +00:00
|
|
|
|
2024-06-25 12:59:02 +00:00
|
|
|
event.replyEmbeds(EmbedUtils.successEmbed()
|
2024-06-26 15:17:57 +00:00
|
|
|
.setDescription("An error occurred while executing the command\n\n" + ex.getLocalizedMessage())
|
|
|
|
.build())
|
|
|
|
.queue();
|
2024-06-24 12:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-28 02:24:55 +00:00
|
|
|
}
|