diff --git a/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java b/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java index 04983095..9064fb3a 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java @@ -1,10 +1,13 @@ package com.discordsrv.common.config.main; import com.discordsrv.common.config.main.generic.DestinationConfig; +import com.discordsrv.common.config.main.generic.GameCommandFilterConfig; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; import java.util.*; +@ConfigSerializable public class ConsoleConfig { @Comment("The console channel or thread") @@ -12,6 +15,9 @@ public class ConsoleConfig { public Appender appender = new Appender(); + public Execution commandExecution = new Execution(); + + @ConfigSerializable public static class Appender { @Comment("The format for log lines") @@ -55,6 +61,39 @@ public class ConsoleConfig { } + @ConfigSerializable + public static class Execution { + + public Execution() { + filters.add( + new GameCommandFilterConfig( + new ArrayList<>(), + false, + new ArrayList<>(Arrays.asList("list", "whitelist")) + ) + ); + filters.add( + new GameCommandFilterConfig( + new ArrayList<>(), + true, + new ArrayList<>(Arrays.asList( + "?", + "op", + "deop", + "execute" + )) + ) + ); + } + + @Comment("At least one condition has to match to allow execution") + public List filters = new ArrayList<>(); + + @Comment("If a command is inputted starting with /, a warning response will be given if this is enabled") + public boolean enableSlashWarning = true; + + } + public enum OutputMode { ANSI("```ansi\n", "```"), LOG("```accesslog\n", "```"), diff --git a/common/src/main/java/com/discordsrv/common/config/main/generic/GameCommandFilterConfig.java b/common/src/main/java/com/discordsrv/common/config/main/generic/GameCommandFilterConfig.java index 4a331daf..4b45ad7d 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/generic/GameCommandFilterConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/generic/GameCommandFilterConfig.java @@ -103,7 +103,7 @@ public class GameCommandFilterConfig { } } if (!match) { - return true; + return false; } for (String configCommand : commands) { diff --git a/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java b/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java index 4cd627eb..a19bd62d 100644 --- a/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java +++ b/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java @@ -1,6 +1,7 @@ package com.discordsrv.common.console; import com.discordsrv.api.DiscordSRVApi; +import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.ConsoleConfig; import com.discordsrv.common.console.entry.LogEntry; @@ -13,6 +14,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -25,6 +28,11 @@ public class ConsoleModule extends AbstractModule implements LogAppe super(discordSRV, new NamedLogger(discordSRV, "CONSOLE")); } + @Override + public @NotNull Collection requiredIntents() { + return Collections.singletonList(DiscordGatewayIntent.MESSAGE_CONTENT); + } + @Override public void enable() { backend = discordSRV.console().loggingBackend(); diff --git a/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java b/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java index 1bf318b1..7a1ae20f 100644 --- a/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java +++ b/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java @@ -1,10 +1,18 @@ package com.discordsrv.common.console; -import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel; +import com.discordsrv.api.discord.entity.DiscordUser; +import com.discordsrv.api.discord.entity.channel.*; +import com.discordsrv.api.discord.entity.guild.DiscordGuildMember; +import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage; import com.discordsrv.api.discord.entity.message.SendableDiscordMessage; +import com.discordsrv.api.discord.events.message.DiscordMessageReceiveEvent; +import com.discordsrv.api.event.bus.Subscribe; import com.discordsrv.api.placeholder.provider.SinglePlaceholder; import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.command.game.GameCommandExecutionHelper; import com.discordsrv.common.config.main.ConsoleConfig; +import com.discordsrv.common.config.main.generic.DestinationConfig; +import com.discordsrv.common.config.main.generic.GameCommandFilterConfig; import com.discordsrv.common.console.entry.LogEntry; import com.discordsrv.common.console.entry.LogMessage; import com.discordsrv.common.console.message.ConsoleMessage; @@ -47,6 +55,88 @@ public class SingleConsoleHandler { this.messageCache = config.appender.useEditing ? new ArrayList<>() : null; timeQueueProcess(); + discordSRV.eventBus().subscribe(this); + } + + @Subscribe + public void onDiscordMessageReceived(DiscordMessageReceiveEvent event) { + DiscordMessageChannel messageChannel = event.getChannel(); + DiscordGuildChannel channel = messageChannel instanceof DiscordGuildChannel ? (DiscordGuildChannel) messageChannel : null; + if (channel == null) { + return; + } + + ReceivedDiscordMessage message = event.getMessage(); + if (message.isFromSelf()) { + return; + } + + String command = event.getMessage().getContent(); + if (command == null) { + return; + } + + DestinationConfig.Single destination = config.channel; + String threadName = destination.threadName; + + DiscordGuildChannel checkChannel; + if (StringUtils.isNotEmpty(threadName)) { + if (!(channel instanceof DiscordThreadChannel)) { + return; + } + + if (!channel.getName().equals(threadName)) { + return; + } + + checkChannel = ((DiscordThreadChannel) channel).getParentChannel(); + } else { + checkChannel = channel; + } + if (checkChannel.getId() != destination.channelId) { + return; + } + + DiscordUser user = message.getAuthor(); + DiscordGuildMember member = message.getMember(); + GameCommandExecutionHelper helper = discordSRV.executeHelper(); + + if (command.startsWith("/") && config.commandExecution.enableSlashWarning) { + // TODO: reply, translation + messageChannel.sendMessage( + SendableDiscordMessage.builder() + .setContent("Your command was prefixed with `/`, but normally commands in the Minecraft server console should **not** begin with `/`") + .build() + ); + } + + boolean pass = false; + for (GameCommandFilterConfig filter : config.commandExecution.filters) { + if (filter.isAcceptableCommand(member, user, command, false, helper)) { + pass = true; + break; + } + } + if (!pass) { + if (!user.isBot()) { + // TODO: reply, translation + messageChannel.sendMessage( + SendableDiscordMessage.builder() + .setContent("You are not allowed to run that command") + .build() + ); + } + return; + } + + // Split message when editing + if (messageCache != null) { + messageCache.clear(); + } + mostRecentMessageId = null; + + // Run the command + discordSRV.console().runCommand(command); } public void queue(LogEntry entry) { @@ -54,6 +144,7 @@ public class SingleConsoleHandler { } public void shutdown() { + discordSRV.eventBus().unsubscribe(this); queueProcessingFuture.cancel(false); queue.clear(); messageCache.clear(); @@ -169,16 +260,16 @@ public class SingleConsoleHandler { DiscordGuildMessageChannel channel = channels.get(0); if (mostRecentMessageId != null) { - long channelId = mostRecentMessageId; + long messageId = mostRecentMessageId; if (isFull) { mostRecentMessageId = null; } - return channel.editMessageById(channelId, sendableMessage); + return channel.editMessageById(messageId, sendableMessage); } return channel.sendMessage(sendableMessage) .whenComplete((receivedMessage, t) -> { - if (receivedMessage != null) { + if (receivedMessage != null && messageCache != null) { mostRecentMessageId = receivedMessage.getId(); } });