refactor: improve getting TrustLevel from discord roles and improve CommandHandlerPlugin (finally)

This commit is contained in:
ChomeNS
2025-05-31 08:34:43 +07:00
parent 23b7c3351a
commit f542bb6fc1
3 changed files with 128 additions and 143 deletions

View File

@@ -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<Role> 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<String, TrustLevel> 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;
}
}

View File

@@ -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<Role> 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)))
);
}
}
}