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.command.impl.guild.beatsaber.scoresaber.ScoreFeedChannelCommand; import cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber.ScoreFeedClearUsersCommand; import cc.fascinated.bat.command.impl.guild.beatsaber.scoresaber.ScoreFeedUserCommand; import cc.fascinated.bat.common.EmbedUtils; import cc.fascinated.bat.model.guild.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 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.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)) .addSubCommand("score-feed-user", context.getBean(ScoreFeedUserCommand.class)) .addSubCommand("score-feed-channel", context.getBean(ScoreFeedChannelCommand.class)) .addSubCommand("score-feed-clear-users", context.getBean(ScoreFeedClearUsersCommand.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 jda.updateCommands().addCommands(commands.values().stream().map(BatCommand::getCommandData).toList()).complete(); 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 try { if (event.getInteraction().getSubcommandName() == null) { command.execute(guild, user, event.getChannel().asTextChannel(), event.getMember(), event.getInteraction()); } else { for (Map.Entry subCommand : command.getSubCommands().entrySet()) { if (subCommand.getKey().equalsIgnoreCase(event.getInteraction().getSubcommandName())) { subCommand.getValue().execute(guild, user, event.getChannel().asTextChannel(), event.getMember(), event.getInteraction()); 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(); } } }