package cc.fascinated.bat.service; import cc.fascinated.bat.command.BatCommand; import cc.fascinated.bat.command.BatSubCommand; import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.LinkSubCommand; import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.ScoreSaberCommand; import cc.fascinated.bat.command.impl.global.beatsaber.scoresaber.UserSubCommand; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.user.BatUser; import lombok.NonNull; import lombok.extern.log4j.Log4j2; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Fascinated (fascinated7) */ @Service @Log4j2 @DependsOn("discordService") public class CommandService extends ListenerAdapter { /** * The registered commands */ private final Map commands = new HashMap<>(); /** * The guild service to use */ private final GuildService guildService; /** * The user service to use */ private final UserService userService; /** * The application context to use */ private final ApplicationContext context; @Autowired public CommandService(@NonNull GuildService guildService, @NonNull UserService userService, @NonNull ApplicationContext context) { this.guildService = guildService; this.userService = userService; this.context = context; DiscordService.JDA.addEventListener(this); // Guild commands // todo: add some, duh // Global commands registerCommand(context.getBean(ScoreSaberCommand.class) .addSubCommand("link", context.getBean(LinkSubCommand.class)) .addSubCommand("user", context.getBean(UserSubCommand.class) )); registerSlashCommands(); // Register all slash commands } /** * Registers a command * * @param command The command to register */ public void registerCommand(@NonNull BatCommand command) { commands.put(command.getName().toLowerCase(), command); } /** * 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 -> { 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 for (BatCommand command : commands.values()) { if (command.getCommandData() == null) { continue; } jda.upsertCommand(command.getCommandData()).complete(); // Register the command on Discord } log.info("Registered all slash commands in {}ms", System.currentTimeMillis() - before); } @Override public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) { Guild discordGuild = event.getGuild(); if (discordGuild == null) { return; } if (event.getUser().isBot()) { return; } if (event.getMember() == null) { return; } String commandName = event.getName(); BatCommand command = commands.get(commandName); if (command == null) { return; } BatGuild guild = guildService.getGuild(discordGuild.getId()); BatUser user = userService.getUser(event.getUser().getId()); // No args provided, use the main command executor List options = event.getInteraction().getOptions(); try { if (options.isEmpty()) { command.execute(guild, user, event.getChannel().asTextChannel(), event.getMember(), event.getInteraction(), null); } // Check if the sub command exists for (Map.Entry subCommand : command.getSubCommands().entrySet()) { for (OptionMapping option : options) { if (subCommand.getKey().equalsIgnoreCase(option.getName())) { subCommand.getValue().execute(guild, user, event.getChannel().asTextChannel(), event.getMember(), event.getInteraction(), option); break; } } } } catch (Exception ex) { log.error("An error occurred while executing command \"{}\"", commandName, ex); event.replyEmbeds(EmbedUtils.buildErrorEmbed("An error occurred while executing the command\n\n" + ex.getLocalizedMessage()).build()).queue(); } } }