From f542bb6fc1b96054f46c837197e9f3dd14acc89b Mon Sep 17 00:00:00 2001 From: ChomeNS <95471003+ChomeNS@users.noreply.github.com> Date: Sat, 31 May 2025 08:34:43 +0700 Subject: [PATCH] refactor: improve getting TrustLevel from discord roles and improve CommandHandlerPlugin (finally) --- build-number.txt | 2 +- .../chomens_bot/command/TrustLevel.java | 25 +- .../plugins/CommandHandlerPlugin.java | 244 +++++++++--------- 3 files changed, 128 insertions(+), 143 deletions(-) diff --git a/build-number.txt b/build-number.txt index 5f7cf203..baba965d 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -3366 \ No newline at end of file +3368 \ No newline at end of file diff --git a/src/main/java/me/chayapak1/chomens_bot/command/TrustLevel.java b/src/main/java/me/chayapak1/chomens_bot/command/TrustLevel.java index a0546a4d..b60ca36e 100644 --- a/src/main/java/me/chayapak1/chomens_bot/command/TrustLevel.java +++ b/src/main/java/me/chayapak1/chomens_bot/command/TrustLevel.java @@ -8,6 +8,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import java.util.List; +import java.util.Map; public enum TrustLevel { PUBLIC(0, Component.text(I18nUtilities.get("trust_level.public"), NamedTextColor.GREEN)), @@ -26,27 +27,21 @@ public enum TrustLevel { } public static TrustLevel fromDiscordRoles (final List roles) { - if (Main.discord == null) return PUBLIC; + if (Main.discord == null || Main.discord.options == null) return PUBLIC; final Configuration.Discord options = Main.discord.options; - if (options == null) return PUBLIC; - - TrustLevel userTrustLevel = PUBLIC; + final Map roleToLevel = Map.of( + options.ownerRoleName.toLowerCase(), OWNER, + options.adminRoleName.toLowerCase(), ADMIN, + options.trustedRoleName.toLowerCase(), TRUSTED + ); for (final Role role : roles) { - if (role.getName().equalsIgnoreCase(options.ownerRoleName)) { - userTrustLevel = OWNER; - break; - } else if (role.getName().equalsIgnoreCase(options.adminRoleName)) { - userTrustLevel = ADMIN; - break; - } else if (role.getName().equalsIgnoreCase(options.trustedRoleName)) { - userTrustLevel = TRUSTED; - break; - } + final TrustLevel level = roleToLevel.get(role.getName().toLowerCase()); + if (level != null) return level; } - return userTrustLevel; + return PUBLIC; } } diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java index 47256cdc..c79f8282 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/CommandHandlerPlugin.java @@ -13,7 +13,6 @@ import me.chayapak1.chomens_bot.data.listener.Listener; import me.chayapak1.chomens_bot.util.ExceptionUtilities; import me.chayapak1.chomens_bot.util.HashingUtilities; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Role; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -73,7 +72,7 @@ public class CommandHandlerPlugin implements Listener { for (final Command command : COMMANDS) { if ( command.name.equals(searchTerm.toLowerCase()) || - Arrays.stream(command.aliases).toList().contains(searchTerm.toLowerCase()) + Arrays.asList(command.aliases).contains(searchTerm.toLowerCase()) ) { return command; } @@ -86,7 +85,7 @@ public class CommandHandlerPlugin implements Listener { private final Bot bot; - private static final AtomicInteger commandsPerSecond = new AtomicInteger(); + private final AtomicInteger commandsPerSecond = new AtomicInteger(); public CommandHandlerPlugin (final Bot bot) { this.bot = bot; @@ -104,27 +103,18 @@ public class CommandHandlerPlugin implements Listener { // and also not really optimized // (sometimes execution time can be as high as 19 ms, // though it can also be as low as 4000 ns) - public void executeCommand ( - final String input, - final CommandContext context - ) { - final boolean inGame = context instanceof PlayerCommandContext; - - final boolean bypass = context instanceof ConsoleCommandContext || context instanceof ChomeNSModCommandContext; - + public void executeCommand (final String input, final CommandContext context) { if (commandsPerSecond.get() > 100) return; else commandsPerSecond.getAndIncrement(); - final String[] splitInput = input.trim().split("\\s+"); + final boolean inGame = context instanceof PlayerCommandContext; + final boolean bypass = isBypassContext(context); + final String[] splitInput = input.trim().split("\\s+"); if (splitInput.length == 0) return; final String commandName = splitInput[0]; - final Command command = findCommand(commandName); - - // I think this is kinda annoying when you correct spelling mistakes or something, - // so I made it return nothing if we're in game if (command == null) { if (!inGame) context.sendOutput( @@ -134,145 +124,145 @@ public class CommandHandlerPlugin implements Listener { Component.text(commandName) ) ); - return; } - if (!bypass) { - if (disabled) { - context.sendOutput(Component.translatable("command_handler.disabled", NamedTextColor.RED)); - return; - } else if ( - context instanceof final PlayerCommandContext playerContext && - command.disallowedPacketTypes != null && - Arrays.asList(command.disallowedPacketTypes).contains(playerContext.packetType) - ) { - return; - } - } + if (!bypass && !isCommandAllowed(context, command)) return; final TrustLevel authenticatedTrustLevel = context.sender.authenticatedTrustLevel; - - final boolean authenticated = context instanceof final PlayerCommandContext playerContext - && playerContext.packetType == ChatPacketType.PLAYER - && authenticatedTrustLevel != TrustLevel.PUBLIC; - - final TrustLevel trustLevel = command.trustLevel; - - if (trustLevel != TrustLevel.PUBLIC && splitInput.length < 2 && inGame && !authenticated) { - context.sendOutput(Component.translatable("command_handler.no_hash_provided", NamedTextColor.RED)); - return; - } - - final boolean needsHash = trustLevel != TrustLevel.PUBLIC && inGame && !authenticated; + final boolean authenticated = isAuthenticated(context, authenticatedTrustLevel); + final boolean needsHash = needsHash(command, inGame, authenticated, splitInput.length); final String userHash = needsHash ? splitInput[1] : ""; - final String[] fullArgs = Arrays.copyOfRange(splitInput, 1, splitInput.length); - final String[] args = needsHash - ? Arrays.copyOfRange(splitInput, 2, splitInput.length) - : fullArgs; + final String[] args = needsHash ? Arrays.copyOfRange(splitInput, 2, splitInput.length) : fullArgs; - // LoL ohio spaghetti code - if (command.trustLevel != TrustLevel.PUBLIC && !bypass) { - if (context instanceof final RemoteCommandContext remote) { - if (remote.source.trustLevel.level < trustLevel.level) { - context.sendOutput( - Component.translatable("command_handler.not_enough_roles", NamedTextColor.RED) - ); - return; - } + if (!checkTrustLevel(context, command, userHash, authenticated, bypass)) + return; - context.trustLevel = remote.source.trustLevel; - } else if (context instanceof final DiscordCommandContext discordCommandContext) { - final Member member = discordCommandContext.member; - - if (member == null) return; - - final List roles = member.getRoles(); - - final TrustLevel userTrustLevel = TrustLevel.fromDiscordRoles(roles); - - if (trustLevel.level > userTrustLevel.level) { - context.sendOutput( - Component - .translatable( - "command_handler.not_enough_roles.trust_level", - Component.text(trustLevel.name()) - ) - .color(NamedTextColor.RED) - ); - return; - } - - context.trustLevel = userTrustLevel; - } else if (authenticated) { - if (trustLevel.level > authenticatedTrustLevel.level) { - context.sendOutput( - Component.translatable("command_handler.not_enough_roles", NamedTextColor.RED) - ); - return; - } - - context.trustLevel = authenticatedTrustLevel; - } else { - final TrustLevel userTrustLevel = HashingUtilities.getTrustLevel(userHash, splitInput[0], context.sender); - - if (trustLevel.level > userTrustLevel.level) { - context.sendOutput( - Component - .translatable( - "command_handler.invalid_hash", - NamedTextColor.RED, - Component.text(trustLevel.toString()) - ) - ); - return; - } - - context.trustLevel = userTrustLevel; - } - } else if (bypass) { - context.trustLevel = TrustLevel.MAX; - } - - // should i give access to all bypass contexts instead of only console? if (!bypass && command.consoleOnly) { context.sendOutput(Component.translatable("command_handler.console_only", NamedTextColor.RED)); return; } - // should these be here? context.fullArgs = fullArgs; context.args = args; context.commandName = command.name; context.userInputCommandName = commandName; + handleExecution(command, context, inGame); + } + + private boolean isBypassContext (final CommandContext context) { + return context instanceof ConsoleCommandContext || context instanceof ChomeNSModCommandContext; + } + + private boolean isAuthenticated (final CommandContext context, final TrustLevel authenticatedTrustLevel) { + return context instanceof final PlayerCommandContext playerContext + && playerContext.packetType == ChatPacketType.PLAYER + && authenticatedTrustLevel != TrustLevel.PUBLIC; + } + + private boolean needsHash (final Command command, final boolean inGame, final boolean authenticated, final int inputLength) { + return command.trustLevel != TrustLevel.PUBLIC && inGame && !authenticated && inputLength < 2; + } + + private boolean checkTrustLevel ( + final CommandContext context, + final Command command, + final String userHash, + final boolean authenticated, + final boolean bypass + ) { + final TrustLevel requiredLevel = command.trustLevel; + + if (requiredLevel == TrustLevel.PUBLIC || bypass) { + context.trustLevel = bypass ? TrustLevel.MAX : TrustLevel.PUBLIC; + return true; + } + + if (context instanceof final RemoteCommandContext remote) { + if (remote.source.trustLevel.level < requiredLevel.level) { + context.sendOutput(Component.translatable("command_handler.not_enough_roles", NamedTextColor.RED)); + return false; + } + context.trustLevel = remote.source.trustLevel; + return true; + } + + if (context instanceof final DiscordCommandContext discord) { + final Member member = discord.member; + if (member == null) return false; + + final TrustLevel userLevel = TrustLevel.fromDiscordRoles(member.getRoles()); + if (userLevel.level < requiredLevel.level) { + context.sendOutput( + Component.translatable( + "command_handler.not_enough_roles.trust_level", + NamedTextColor.RED, + Component.text(requiredLevel.name()) + ) + ); + return false; + } + context.trustLevel = userLevel; + return true; + } + + if (authenticated) { + final TrustLevel authLevel = context.sender.authenticatedTrustLevel; + if (authLevel.level < requiredLevel.level) { + context.sendOutput(Component.translatable("command_handler.not_enough_roles", NamedTextColor.RED)); + return false; + } + context.trustLevel = authLevel; + return true; + } + + final TrustLevel hashLevel = HashingUtilities.getTrustLevel(userHash, command.name, context.sender); + if (hashLevel.level < requiredLevel.level) { + context.sendOutput( + Component.translatable( + "command_handler.invalid_hash", + NamedTextColor.RED, + Component.text(requiredLevel.toString()) + ) + ); + return false; + } + + context.trustLevel = hashLevel; + return true; + } + + private boolean isCommandAllowed (final CommandContext context, final Command command) { + if (disabled) { + context.sendOutput(Component.translatable("command_handler.disabled", NamedTextColor.RED)); + return false; + } + + return !(context instanceof final PlayerCommandContext playerContext) || + command.disallowedPacketTypes == null || + !Arrays.asList(command.disallowedPacketTypes).contains(playerContext.packetType); + } + + private void handleExecution (final Command command, final CommandContext context, final boolean inGame) { try { final Component output = command.execute(context); - if (output != null) context.sendOutput(output); } catch (final CommandException e) { - context.sendOutput(e.message.color(NamedTextColor.RED)); + context.sendOutput(e.message.colorIfAbsent(NamedTextColor.RED)); } catch (final Exception e) { bot.logger.error(e); - final String stackTrace = ExceptionUtilities.getStacktrace(e); - if (inGame) { - if (bot.options.useChat || !bot.options.useCore) - context.sendOutput(Component.text(e.toString()).color(NamedTextColor.RED)); - else - context.sendOutput( - Component - .translatable("command_handler.exception", NamedTextColor.RED) - .hoverEvent( - HoverEvent.showText( - Component.text(stackTrace, NamedTextColor.RED) - ) - ) - ); + if (inGame && (bot.options.useChat || !bot.options.useCore)) { + context.sendOutput(Component.text(e.toString(), NamedTextColor.RED)); } else { - context.sendOutput(Component.text(stackTrace, NamedTextColor.RED)); + context.sendOutput( + Component + .translatable("command_handler.exception", NamedTextColor.RED) + .hoverEvent(HoverEvent.showText(Component.text(stackTrace, NamedTextColor.RED))) + ); } } }