From cd1c8e3c9ded778ac4e6b479d05b1525a9f9c3a7 Mon Sep 17 00:00:00 2001 From: Joshua Primm Date: Wed, 14 Aug 2024 07:36:40 -0500 Subject: [PATCH] feat: add regex matching feature for restricted and default servers (#247) * Add regex matching feature for restricted and default servers * Fix tests --- .../william278/huskchat/channel/Channel.java | 5 +- .../william278/huskchat/config/Channels.java | 14 ++++ .../huskchat/listener/PlayerListener.java | 9 +-- .../huskchat/message/ChatMessage.java | 8 +- .../huskchat/channel/ChannelTests.java | 73 +++++++++++++++++++ 5 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 common/src/test/java/net/william278/huskchat/channel/ChannelTests.java diff --git a/common/src/main/java/net/william278/huskchat/channel/Channel.java b/common/src/main/java/net/william278/huskchat/channel/Channel.java index f1a3a86..ea12c5a 100644 --- a/common/src/main/java/net/william278/huskchat/channel/Channel.java +++ b/common/src/main/java/net/william278/huskchat/channel/Channel.java @@ -20,6 +20,7 @@ package net.william278.huskchat.channel; import de.exlll.configlib.Configuration; +import java.util.regex.Pattern; import lombok.*; import net.william278.huskchat.config.Settings; import net.william278.huskchat.user.OnlineUser; @@ -136,7 +137,9 @@ public List getShortcutCommands() { } public boolean isServerRestricted(@NotNull String server) { - return restrictedServers.stream().anyMatch(server::equalsIgnoreCase); + return restrictedServers.stream().anyMatch( + restrictedServer -> Pattern.compile(restrictedServer, Pattern.CASE_INSENSITIVE) + .matcher(server).matches()); } public boolean canUserSend(@NotNull OnlineUser user) { diff --git a/common/src/main/java/net/william278/huskchat/config/Channels.java b/common/src/main/java/net/william278/huskchat/config/Channels.java index 5d8702e..011cdfd 100644 --- a/common/src/main/java/net/william278/huskchat/config/Channels.java +++ b/common/src/main/java/net/william278/huskchat/config/Channels.java @@ -21,6 +21,8 @@ import de.exlll.configlib.Comment; import de.exlll.configlib.Configuration; +import java.util.Map.Entry; +import java.util.regex.Pattern; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -113,6 +115,18 @@ public Optional getChannel(@Nullable String channelId) { return channels.stream().filter(channel -> channel.getId().equalsIgnoreCase(channelId)).findFirst(); } + /** + * Gets the default channel for the given server. Falls back to the global default is a server-specific default does not exist. + * @param server The server name + * @return The default channel for the given server, if any + */ + public Optional getServerDefaultChannel(String server) { + return getServerDefaultChannels().entrySet().stream().filter( + defaultChannelEntry -> Pattern.compile(defaultChannelEntry.getKey(), + Pattern.CASE_INSENSITIVE).matcher(server).matches()).map(Entry::getValue) + .findFirst(); + } + @NotNull public List getChannelCommandAliases() { return Settings.formatCommands(channelCommandAliases); diff --git a/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java b/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java index 53c4eb1..b10b7b3 100644 --- a/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java +++ b/common/src/main/java/net/william278/huskchat/listener/PlayerListener.java @@ -36,9 +36,9 @@ public abstract class PlayerListener { // Handle server switches public final void handlePlayerSwitchServer(@NotNull OnlineUser player, @NotNull String newServer) { // Switch to the default channel for the server if there is one - final Map defaultChannels = plugin.getChannels().getServerDefaultChannels(); - if (defaultChannels.containsKey(newServer)) { - plugin.editUserCache(c -> c.switchPlayerChannel(player, defaultChannels.get(newServer), plugin)); + final Optional defaultChannel = plugin.getChannels().getServerDefaultChannel(newServer); + if (defaultChannel.isPresent()) { + plugin.editUserCache(c -> c.switchPlayerChannel(player, defaultChannel.get(), plugin)); return; } @@ -52,8 +52,7 @@ public final void handlePlayerSwitchServer(@NotNull OnlineUser player, @NotNull // Switch the player's channel away if their current channel is now restricted plugin.getChannels().getChannels().stream() .filter(channel -> channel.getId().equalsIgnoreCase(currentChannel.get())) - .findFirst().flatMap(channel -> channel.getRestrictedServers().stream() - .filter(restrictedServer -> restrictedServer.equalsIgnoreCase(newServer)).findFirst()) + .findFirst().filter(channel -> channel.isServerRestricted(newServer)) .ifPresent(restricted -> plugin.editUserCache(c -> c .switchPlayerChannel(player, plugin.getChannels().getDefaultChannel(), plugin))); } diff --git a/common/src/main/java/net/william278/huskchat/message/ChatMessage.java b/common/src/main/java/net/william278/huskchat/message/ChatMessage.java index 1a31506..8c1ade8 100644 --- a/common/src/main/java/net/william278/huskchat/message/ChatMessage.java +++ b/common/src/main/java/net/william278/huskchat/message/ChatMessage.java @@ -72,11 +72,9 @@ public boolean dispatch() { } // Verify that the player is not sending a message from a server where channel access is restricted - for (String restrictedServer : channel.get().getRestrictedServers()) { - if (restrictedServer.equalsIgnoreCase(getSender().getServerName())) { - getPlugin().getLocales().sendMessage(getSender(), "error_channel_restricted_server", channel.get().getId()); - return true; - } + if (channel.get().isServerRestricted(getSender().getServerName())) { + getPlugin().getLocales().sendMessage(getSender(), "error_channel_restricted_server", channel.get().getId()); + return true; } // Determine the players who will receive the message; diff --git a/common/src/test/java/net/william278/huskchat/channel/ChannelTests.java b/common/src/test/java/net/william278/huskchat/channel/ChannelTests.java new file mode 100644 index 0000000..912bacc --- /dev/null +++ b/common/src/test/java/net/william278/huskchat/channel/ChannelTests.java @@ -0,0 +1,73 @@ +/* + * This file is part of HuskChat, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.huskchat.channel; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ChannelTests { + Channel plainTextChannel = Channel + .builder().id("plaintext").restrictedServers(List.of("plain", "text", "restrictions")).build(); + Channel regexTextChannel = Channel + .builder().id("regex").restrictedServers(List.of(".*regex.*", "matcher.*", ".*channel")).build(); + + @Test + public void testPlaintextUnrestrictedServer() { + Assertions.assertFalse(plainTextChannel.isServerRestricted("nota")); + Assertions.assertFalse(plainTextChannel.isServerRestricted("plaintext")); + Assertions.assertFalse(plainTextChannel.isServerRestricted("restricted server")); + } + + @Test + public void testPlaintextRestrictedServer() { + Assertions.assertTrue(plainTextChannel.isServerRestricted("plain")); + Assertions.assertTrue(plainTextChannel.isServerRestricted("text")); + Assertions.assertTrue(plainTextChannel.isServerRestricted("restrictions")); + } + + @Test + public void testPlaintextRestrictedServerIgnoreCase() { + Assertions.assertTrue(plainTextChannel.isServerRestricted("PLAIN")); + Assertions.assertTrue(plainTextChannel.isServerRestricted("tExT")); + Assertions.assertTrue(plainTextChannel.isServerRestricted("resTriCTioNs")); + } + + @Test + public void testRegexUnrestrictedServer() { + Assertions.assertFalse(regexTextChannel.isServerRestricted("does")); + Assertions.assertFalse(regexTextChannel.isServerRestricted("not")); + Assertions.assertFalse(regexTextChannel.isServerRestricted("match")); + } + + @Test + public void testRegexRestrictedServer() { + Assertions.assertTrue(regexTextChannel.isServerRestricted("xxx-regex-1234")); + Assertions.assertTrue(regexTextChannel.isServerRestricted("matcher-funtime")); + Assertions.assertTrue(regexTextChannel.isServerRestricted("super-channel")); + } + + @Test + public void testRegexRestrictedServerIgnoreCase() { + Assertions.assertTrue(regexTextChannel.isServerRestricted("xXx-REGEX-1234")); + Assertions.assertTrue(regexTextChannel.isServerRestricted("maTCher-funtime")); + Assertions.assertTrue(regexTextChannel.isServerRestricted("sUPEr-chANnel")); + } +}