refactor: use language on DirectMessageEventHandler refactor: some misc refactors (i forgot what i did lol)
280 lines
10 KiB
Java
280 lines
10 KiB
Java
package me.chayapak1.chomens_bot.plugins;
|
|
|
|
import it.unimi.dsi.fastutil.objects.ObjectList;
|
|
import me.chayapak1.chomens_bot.Bot;
|
|
import me.chayapak1.chomens_bot.command.Command;
|
|
import me.chayapak1.chomens_bot.command.CommandContext;
|
|
import me.chayapak1.chomens_bot.command.CommandException;
|
|
import me.chayapak1.chomens_bot.command.TrustLevel;
|
|
import me.chayapak1.chomens_bot.command.contexts.*;
|
|
import me.chayapak1.chomens_bot.commands.*;
|
|
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
|
|
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;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
public class CommandHandlerPlugin implements Listener {
|
|
public static final List<Command> COMMANDS = ObjectList.of(
|
|
new CommandBlockCommand(),
|
|
new CowsayCommand(),
|
|
new EchoCommand(),
|
|
new HelpCommand(),
|
|
new TestCommand(),
|
|
new ValidateCommand(),
|
|
new MusicCommand(),
|
|
new RandomTeleportCommand(),
|
|
new BotVisibilityCommand(),
|
|
new TPSBarCommand(),
|
|
new NetMessageCommand(),
|
|
new RefillCoreCommand(),
|
|
new WikipediaCommand(),
|
|
new UrbanCommand(),
|
|
new ClearChatCommand(),
|
|
new ListCommand(),
|
|
new ServerEvalCommand(),
|
|
new UUIDCommand(),
|
|
new TimeCommand(),
|
|
new BruhifyCommand(),
|
|
new EndCommand(),
|
|
new CloopCommand(),
|
|
new WeatherCommand(),
|
|
new TranslateCommand(),
|
|
new KickCommand(),
|
|
new ClearChatQueueCommand(),
|
|
new FilterCommand(),
|
|
new MailCommand(),
|
|
new EvalCommand(),
|
|
new InfoCommand(),
|
|
new ConsoleCommand(),
|
|
// new ScreenshareCommand(),
|
|
new WhitelistCommand(),
|
|
new SeenCommand(),
|
|
new IPFilterCommand(),
|
|
new StopCommand(),
|
|
new GrepLogCommand(),
|
|
new FindAltsCommand(),
|
|
new RestartCommand(),
|
|
new NetCommandCommand(),
|
|
new AuthCommand()
|
|
);
|
|
|
|
public static Command findCommand (final String searchTerm) {
|
|
if (searchTerm.isBlank()) return null;
|
|
|
|
for (final Command command : COMMANDS) {
|
|
if (
|
|
command.name.equals(searchTerm.toLowerCase()) ||
|
|
Arrays.stream(command.aliases).toList().contains(searchTerm.toLowerCase())
|
|
) {
|
|
return command;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public boolean disabled = false;
|
|
|
|
private final Bot bot;
|
|
|
|
private static final AtomicInteger commandsPerSecond = new AtomicInteger();
|
|
|
|
public CommandHandlerPlugin (final Bot bot) {
|
|
this.bot = bot;
|
|
|
|
bot.listener.addListener(this);
|
|
}
|
|
|
|
@Override
|
|
public void onLocalSecondTick () {
|
|
commandsPerSecond.set(0);
|
|
}
|
|
|
|
// BETTER QUALITY than the js version
|
|
// BUT still not the best
|
|
// 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;
|
|
|
|
if (commandsPerSecond.get() > 100) return;
|
|
else commandsPerSecond.getAndIncrement();
|
|
|
|
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(
|
|
Component.translatable(
|
|
"command_handler.unknown_command",
|
|
NamedTextColor.RED,
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
|
|
try {
|
|
final Component output = command.execute(context);
|
|
|
|
if (output != null) context.sendOutput(output);
|
|
} catch (final CommandException e) {
|
|
context.sendOutput(e.message.color(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)
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
context.sendOutput(Component.text(stackTrace, NamedTextColor.RED));
|
|
}
|
|
}
|
|
}
|
|
}
|