Bat/src/main/java/cc/fascinated/bat/service/CommandService.java

245 lines
11 KiB
Java
Raw Normal View History

2024-06-24 12:56:01 +00:00
package cc.fascinated.bat.service;
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-30 03:13:54 +00:00
import cc.fascinated.bat.config.Config;
2024-06-30 04:15:37 +00:00
import cc.fascinated.bat.features.FeatureProfile;
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;
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)
*/
@Service
@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();
2024-06-30 03:13:54 +00:00
Guild adminGuild = jda.getGuildById(Consts.ADMIN_GUILD);
if (!Config.isProduction()) {
if (adminGuild == null) {
log.error("Unable to find the admin guild to register commands");
return;
}
jda.retrieveCommands().complete().forEach(command -> jda.deleteCommandById(command.getId()).complete());
2024-06-30 04:15:37 +00:00
List<Command> registeredCommands = adminGuild.updateCommands().addCommands(commands.values().stream().map(BatCommand::getCommandData).toList()).complete();
log.info("Registered {} slash commands in {}ms (DEV MODE)", registeredCommands.size(), System.currentTimeMillis() - before);
2024-06-30 03:13:54 +00:00
return;
}
2024-06-24 12:56:01 +00:00
// Unregister all commands that Discord has but we don't
jda.retrieveCommands().complete().forEach(command -> {
if (commands.containsKey(command.getName())
&& (commands.get(command.getName()).getCommandInfo().category().isHidden()
|| commands.get(command.getName()).getCommandInfo().botOwnerOnly())) {
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());
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
List<Command> discordCommands = jda.updateCommands().addCommands(commands.values().stream()
2024-06-27 20:12:31 +00:00
.filter(command -> !command.getCategory().isHidden() || !command.isBotOwnerOnly())
.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
}
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");
}
log.info("Registered {} slash commands in {}ms", discordCommands.size(), System.currentTimeMillis() - before);
2024-06-24 12:56:01 +00:00
}
/**
* Gets commands that are in a specific category
*
* @param category The category
* @return The commands
*/
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-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());
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 {
BatCommandExecutor executor = null;
2024-06-28 18:11:37 +00:00
CommandInfo commandInfo = command.getCommandInfo();
List<Permission> requiredPermissions = new ArrayList<>();
boolean isSubCommand = false;
2024-06-25 10:14:12 +00:00
// No args provided, use the main command executor
if (event.getInteraction().getSubcommandName() == null) {
executor = command;
2024-06-27 18:36:52 +00:00
requiredPermissions.addAll(Arrays.asList(command.getCommandInfo().requiredPermissions()));
} 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
isSubCommand = true;
2024-06-28 18:11:37 +00:00
commandInfo = subCommand.getValue().getCommandInfo();
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;
}
// Check if the user has the required permissions
2024-06-26 19:41:31 +00:00
if (ranInsideGuild && event.getMember() != null) {
List<Permission> missingPermissions = new ArrayList<>();
for (Permission permission : requiredPermissions) {
if (!event.getMember().hasPermission(permission)) {
missingPermissions.add(permission);
}
}
if (!missingPermissions.isEmpty()) {
StringBuilder missing = new StringBuilder();
for (Permission permission : missingPermissions) {
missing.append("`").append(permission.getName()).append("`").append(", ");
}
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You are missing the following permissions to execute this command:\n" +
missing.substring(0, missing.length() - 2))
.build())
.setEphemeral(true)
.queue();
return;
2024-06-24 12:56:01 +00:00
}
}
2024-06-28 18:11:37 +00:00
if (isSubCommand && commandInfo.guildOnly() && !ranInsideGuild) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("This command can only be executed in a guild")
.build()).setEphemeral(true).queue();
return;
}
2024-06-30 04:15:37 +00:00
if (guild != null) {
FeatureProfile featureProfile = guild.getFeatureProfile();
if (featureProfile.isFeatureDisabled(command.getFeature())) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("This command has been disabled by the guild owner")
.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) {
2024-06-30 00:03:10 +00:00
log.error("An error occurred while executing command \"{}\"", commandName, ex);
2024-06-29 21:38:53 +00:00
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription(ex.getLocalizedMessage())
.build())
2024-06-29 21:38:53 +00:00
.setEphemeral(true)
.queue();
2024-06-24 12:56:01 +00:00
}
}
2024-06-28 02:24:55 +00:00
}