diff --git a/.gitignore b/.gitignore index 4788b4b..689d6fe 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ # IntelliJ out/ +# Gradle +.gradle/ +build/ + # Compiled class file *.class diff --git a/README.md b/README.md index db7c83c..f51844b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,49 @@ # ChatFilter -Chat Filter is a chat management plugin for reducing spam, swearing, advertisement of IPs and URLs. Stop repetition in chat and add as many swear words, IPs and URLs as you want to make sure your server chat is controlled! This plugin also filters books, signs, commands and anvils. +Chat Filter is a chat management plugin for reducing spam, swearing, advertisement of IPs and URLs. Stop repetition in chat and add as many swear words, IPs and URLs as you want to make sure your server chat is controlled! This plugin also filters books, signs, commands and anvils. More details below! + +## Features +- **Regex filtering:** Utilizes the use of regex to prevent bypassing of the filter. +- **Multiple Filters:** Filter words, IPs and URLs covering chat, anvils, signs, commands and books. +- **Unicode support:** Prevents the use of certain Unicode common with hacked clients. (hacker font) +- **Anti Spam:** Stop repetitive or similar messages aswell as excessive CAPS and character spam. (Player names exempt) +- **Punishments:** Execute commands when a player is caught by the filter. +- **Commands:** In-game regex generator for words, whitelisting of words, pause and clear chat. +- **Notify staff:** Alerts and highlights swearing and advertising to players with the correct permission. +- **Customizability:** All messages are fully modifiable - Hex compatible. +- **Preset words:** Over 55+ preset English words. +- **No bloat features:** ChatFilter has been made to stay simple and basic. +- **Languages files:** ChatFilter currently supports English, Danish, Chinese (Thanks to Zhaomengran) and Spanish. + +## Commands +- `/clearchat (/cf clear)` – Clears the chat. +- `/cf help` – Displays a list of the plugin’s commands. +- `/cf reload` – Reloads the plugin config. +- `/cf blacklist (ip/word) list/add/remove ` – Blacklists a chosen word or IP. (Will not allow this word/IP to go throught the filter) +- `/cf whitelist (ip/word) list/add/remove ` – Whitelists a chosen word or IP. (Will allow a string of characters/a word or IP to go through the filter) +- `/cf import` - Import plain text words +- `/cf pause` – Pause chat for players that do not have the bypass perm. + +## Permissions +- `chatfilter.reload` – Allows players to use /cf reload +- `chatfilter.blacklist` – Allows players to use /cf blacklist +- `chatfilter.whitelist` – Allows players to use /cf whitelist +- `chatfilter.blacklist.remove` – Allows players to use /cf whitelist remove +- `chatfilter.whitelist.remove` – Allows players to use /cf whitelist remove +- `chatfilter.view` – Allows players to to view what gets caught in the filter +- `chatfilter.pause` – Allows players to pause the chat +- `chatfilter.bypass` – Allows the player to bypass all filters(Chat, Signs, Books, Anvils, Decaps, pause chat and repeat messages) +- `chatfilter.bypass.chat` – Allows the player to bypass in chat +- `chatfilter.bypass.sign` – Allows the player to bypass on a sign +- `chatfilter.bypass.anvil` – Allows the player to bypass in a anvil +- `chatfilter.bypass.book` – Allows the player to bypass in books +- `chatfilter.bypass.command` – Allows the player to bypass in commands +- `chatfilter.bypass.repeat` – Allows the player to bypass repeat messages +- `chatfilter.bypass.caps` –Allows the player to bypass chat decapping +- `chatfilter.bypass.pause` – Allows the player to bypass paused chat +- `chatfilter.bypass.swear` – Allows the player to bypass all swear filters (chat ,books, commands etc) +- `chatfilter.bypass.swear.` – Allows the player to bypass set config entries (chat ,books, commands etc) +- `chatfilter.bypass.ip.` – Allows the player to bypass set config entries (chat ,books, commands etc) +- `chatfilter.bypass.ip` – Allows the player to bypass all ip filters (chat ,books, commands etc) + +# DISCLAIMER +This fork implements Folia with minimal code changes. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..f3ef336 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + java + id("com.gradleup.shadow") version "9.4.1" + id("io.papermc.paperweight.userdev") version "2.0.0-beta.21" +} + +group = "a4.papers.chatfilter" +version = "2.1.1" + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://oss.sonatype.org/content/groups/public/") + maven("https://jitpack.io") +} + +dependencies { + paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT") + implementation("com.github.Anon8281:UniversalScheduler:0.1.7") +} + +tasks { + compileJava { + options.encoding = "UTF-8" + } + + processResources { + filesMatching("plugin.yml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveClassifier.set("") // Produces plain jar + // Use runtimeClasspath instead of implementation to fix Gradle 8+ issue + configurations = listOf(project.configurations.runtimeClasspath.get()) + relocate( + "com.github.Anon8281.universalScheduler", + "a4.papers.chatfilter.universalScheduler" + ) + } + + build { + dependsOn(shadowJar) + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 91f2d86..0000000 --- a/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - 4.0.0 - - a4.papers.chatfilter - chatFilter - 1.1.8 - jar - - ChatFilter - - - 1.8 - UTF-8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - ${java.version} - ${java.version} - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - false - C:\Users\A4pap\Desktop\Output\ChatFilter.jar - - - - - - - - src/main/resources - true - - - - - - - spigotmc-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - - sonatype - https://oss.sonatype.org/content/groups/public/ - - - - - - org.spigotmc - spigot-api - 1.18.1-R0.1-SNAPSHOT - provided - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..9ca48bb --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,8 @@ +rootProject.name = "ChatFilter" + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") + } +} \ No newline at end of file diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java b/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java index 5a20647..89af166 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java @@ -1,19 +1,17 @@ package a4.papers.chatfilter.chatfilter; -import a4.papers.chatfilter.chatfilter.commands.ClearChatCommand; -import a4.papers.chatfilter.chatfilter.commands.CommandHandler; -import a4.papers.chatfilter.chatfilter.commands.CommandMain; -import a4.papers.chatfilter.chatfilter.commands.TabComplete; -import a4.papers.chatfilter.chatfilter.events.*; -import a4.papers.chatfilter.chatfilter.shared.ChatFilters; -import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; -import a4.papers.chatfilter.chatfilter.shared.Types; -import a4.papers.chatfilter.chatfilter.shared.UnicodeWrapper; -import a4.papers.chatfilter.chatfilter.shared.lang.LangManager; -import a4.papers.chatfilter.chatfilter.shared.regexHandler.LoadFilters; -import a4.papers.chatfilter.chatfilter.shared.regexHandler.RegexpGenerator; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import io.papermc.paper.event.player.AsyncChatEvent; +import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -22,24 +20,45 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerEditBookEvent; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.regex.Pattern; +import com.github.Anon8281.universalScheduler.UniversalScheduler; +import com.github.Anon8281.universalScheduler.scheduling.schedulers.TaskScheduler; + +import a4.papers.chatfilter.chatfilter.commands.ClearChatCommand; +import a4.papers.chatfilter.chatfilter.commands.CommandHandler; +import a4.papers.chatfilter.chatfilter.commands.CommandMain; +import a4.papers.chatfilter.chatfilter.commands.TabComplete; +import a4.papers.chatfilter.chatfilter.events.AnvilListener; +import a4.papers.chatfilter.chatfilter.events.BooksListener; +import a4.papers.chatfilter.chatfilter.events.CapsChatListener; +import a4.papers.chatfilter.chatfilter.events.ChatDelayListener; +import a4.papers.chatfilter.chatfilter.events.CommandListener; +import a4.papers.chatfilter.chatfilter.events.PauseChat; +import a4.papers.chatfilter.chatfilter.events.RepeatCharListener; +import a4.papers.chatfilter.chatfilter.events.SignListener; +import a4.papers.chatfilter.chatfilter.events.SwearChatListener; +import a4.papers.chatfilter.chatfilter.shared.ChatFilters; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.UnicodeWrapper; +import a4.papers.chatfilter.chatfilter.shared.lang.LangManager; +import a4.papers.chatfilter.chatfilter.shared.regexHandler.LoadFilters; +import a4.papers.chatfilter.chatfilter.shared.regexHandler.RegexpGenerator; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +@SuppressWarnings("deprecation") public class ChatFilter extends JavaPlugin { + private static TaskScheduler scheduler; + private static ChatFilter instance; + private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacyAmpersand(); + private static final PlainTextComponentSerializer PLAIN_TEXT_SERIALIZER = PlainTextComponentSerializer.plainText(); + public ConsoleCommandSender consoleSender = Bukkit.getConsoleSender(); public ChatFilter chatFilter; public ChatFilters chatFilters; @@ -69,6 +88,7 @@ public class ChatFilter extends JavaPlugin { public boolean CommandsOnSwearAndAdvertisesEnabled; public boolean settingsAllowURL; public boolean settingsBlockFancyChat; + public boolean settingsBlockCustomSybols; public boolean CommandsOnFontEnabled; public boolean deCap; public boolean chatPause = false; @@ -103,9 +123,11 @@ public class ChatFilter extends JavaPlugin { public String cancelChatReplace; public String URL_REGEX; public String perWordOptionsString; + private String antiSpamReplacementToken = "$1"; public Pattern antiSpamPattern; - private Integer blockedInt = 1; + public Pattern urlPattern; + private int blockedInt = 1; private File wordConfigFile; private File advertConfigFile; private File whitelistConfigFile; @@ -116,6 +138,7 @@ public class ChatFilter extends JavaPlugin { private FileConfiguration unicodeConfig; + @Override public void onEnable() { regexWords = new HashMap<>(); regexAdvert = new HashMap<>(); @@ -132,6 +155,10 @@ public void onEnable() { } catch (MalformedURLException e) { e.printStackTrace(); } + + scheduler = UniversalScheduler.getScheduler(this); + instance = this; + createCustomConfig(); loadVariables(); getCommand("chatfilter").setExecutor(new CommandMain(this)); @@ -147,15 +174,15 @@ public void onEnable() { PauseChat pc = new PauseChat(this); CommandListener cl = new CommandListener(this); RepeatCharListener rcl = new RepeatCharListener(this); - pm.registerEvent(AsyncPlayerChatEvent.class, scl, EventPriority.valueOf(getConfig().getString("EventPriority.SwearListener")), scl, this, true); - pm.registerEvent(AsyncPlayerChatEvent.class, ccl, EventPriority.valueOf(getConfig().getString("EventPriority.CapsListener")), ccl, this, true); + pm.registerEvent(AsyncChatEvent.class, scl, EventPriority.valueOf(getConfig().getString("EventPriority.SwearListener")), scl, this, true); + pm.registerEvent(AsyncChatEvent.class, ccl, EventPriority.valueOf(getConfig().getString("EventPriority.CapsListener")), ccl, this, true); pm.registerEvent(PlayerEditBookEvent.class, bl, EventPriority.valueOf(getConfig().getString("EventPriority.BookListener")), bl, this, true); pm.registerEvent(SignChangeEvent.class, sl, EventPriority.valueOf(getConfig().getString("EventPriority.SignListener")), sl, this, true); pm.registerEvent(InventoryClickEvent.class, al, EventPriority.valueOf(getConfig().getString("EventPriority.AdvilListener")), al, this, true); - pm.registerEvent(AsyncPlayerChatEvent.class, cdl, EventPriority.valueOf(getConfig().getString("EventPriority.ChatDelayListener")), cdl, this, true); - pm.registerEvent(AsyncPlayerChatEvent.class, pc, EventPriority.valueOf(getConfig().getString("EventPriority.PauseChatListener")), pc, this, true); + pm.registerEvent(AsyncChatEvent.class, cdl, EventPriority.valueOf(getConfig().getString("EventPriority.ChatDelayListener")), cdl, this, true); + pm.registerEvent(AsyncChatEvent.class, pc, EventPriority.valueOf(getConfig().getString("EventPriority.PauseChatListener")), pc, this, true); pm.registerEvent(PlayerCommandPreprocessEvent.class, cl, EventPriority.valueOf(getConfig().getString("EventPriority.CommandListener")), cl, this, true); - pm.registerEvent(AsyncPlayerChatEvent.class, rcl, EventPriority.valueOf(getConfig().getString("EventPriority.RepeatCharListener")), rcl, this, true); + pm.registerEvent(AsyncChatEvent.class, rcl, EventPriority.valueOf(getConfig().getString("EventPriority.RepeatCharListener")), rcl, this, true); saveDefaultConfig(); getFilters().loadWordFilter(); getFilters().loadAdvertFilter(); @@ -173,16 +200,20 @@ public void onEnable() { metrics.addCustomChart(new Metrics.SimplePie("total_filters", () -> { return String.valueOf(regexWords.size() + regexAdvert.size()); })); - metrics.addCustomChart(new Metrics.SingleLineChart("block", new Callable() { - @Override - public Integer call() throws Exception { - return blockedInt; - } - })); + metrics.addCustomChart(new Metrics.SingleLineChart("block", () -> blockedInt)); } @Override public void onDisable() { + Bukkit.getScheduler().cancelTasks(this); + scheduler = null; + instance = null; + wordRegexPattern.clear(); + advertRegexPattern.clear(); + regexWords.clear(); + regexAdvert.clear(); + unicodeBlacklist.clear(); + unicodeWhitelist.clear(); saveDefaultConfig(); } @@ -199,6 +230,7 @@ public void loadVariables() { this.CommandsOnSwearAndAdvertisesCommand = getConfig().getString("CommandsOnSwearAndAdvertises.command"); this.settingsAllowURL = getConfig().getBoolean("settings.allowURL"); this.settingsBlockFancyChat = getConfig().getBoolean("settings.blockFancyChat"); + this.settingsBlockCustomSybols = getConfig().getBoolean("settings.blockCustomSybols"); this.settingsSwearHighLight = getConfig().getString("settings.swearHighLight"); this.cmdCheck = getConfig().getBoolean("checkCommands"); this.capsAmount = getConfig().getInt("settings.capsAmount"); @@ -211,6 +243,8 @@ public void loadVariables() { this.enableLeetSpeak = getConfig().getBoolean("enableLeetSpeak"); this.antiSpamEnabled = getConfig().getBoolean("antiSpam.enable"); this.antiSpamReplaceAmount = getConfig().getInt("antiSpam.replaceAmount"); + int safeRepeatAmount = Math.max(1, this.antiSpamReplaceAmount); + this.antiSpamReplacementToken = "$1".repeat(safeRepeatAmount); this.repeatDelay = getConfig().getInt("settings.repeatDelay"); this.defaultWordEnabled = getConfig().getBoolean("default.word.Enabled"); @@ -230,15 +264,18 @@ public void loadVariables() { this.defaultIPCancelReplaceWith = getConfig().getString("default.ip.CancelChat.ReplaceWith"); this.defaultIPAction = getConfig().getStringList("default.ip.Action"); this.antiSpamPattern = Pattern.compile("(\\S)\\1{" + getConfig().getInt("antiSpam.aboveAmount") + ",}"); + this.urlPattern = Pattern.compile(this.URL_REGEX); this.perWordOptionsEnable = getConfig().getBoolean("perWordOptions.enabled"); this.perWordOptionsString = getConfig().getString("perWordOptions.nameOfList"); } public String colour(String s) { if (manager.supported("hex")) { - return manager.colorStringHex(s); + String result = Manager.colorStringHex(s); + return result; } else { - return ChatColor.translateAlternateColorCodes('&', s); + String deserialized = LEGACY_SERIALIZER.serialize(LEGACY_SERIALIZER.deserialize(s)); + return deserialized; } } @@ -279,7 +316,7 @@ public RegexpGenerator regexpGenerator() { } public void sendStaffMessage(String str) { - for (Player online : Bukkit.getServer().getOnlinePlayers()) { + for (Player online : Bukkit.getOnlinePlayers()) { if (online.hasPermission("chatfilter.view")) { online.sendMessage(str); } @@ -288,7 +325,7 @@ public void sendStaffMessage(String str) { public void sendConsole(Types type, String msg, Player p, String regexUsed, String pl) { if (!type.equals(Types.NOTYPE)) { - blockedInt = new Integer(blockedInt.intValue() + 1); + blockedInt++; consoleSender.sendMessage("------- Match Type: " + type.id + " ~ " + pl.toUpperCase()); consoleSender.sendMessage("Match: " + regexUsed); consoleSender.sendMessage("Catch > " + p.getName() + ": " + msg); @@ -349,4 +386,47 @@ public void reloadConfigs() throws Exception { wordConfig.load(wordConfigFile); unicodeConfig.load(unicodeConfigFile); } + + public static Object runTask(Runnable runnable) { + if (scheduler != null) { + return scheduler.runTask(runnable); + } else { + Bukkit.getScheduler().runTask(instance, runnable); + return null; + } + } + + public static Object runTaskLater(Runnable runnable, long delay) { + if (scheduler != null) { + return scheduler.runTaskLater(runnable, delay); + } else { + Bukkit.getScheduler().runTaskLater(instance, runnable, delay); + return null; + } + } + + public static Object runTaskTimer(Runnable runnable, long delay, long period) { + if (scheduler != null) { + return scheduler.runTaskTimer(runnable, delay, period); + } else { + Bukkit.getScheduler().runTaskTimer(instance, runnable, delay, period); + return null; + } + } + + public static TaskScheduler getScheduler() { + return scheduler; + } + + public String getAntiSpamReplacementToken() { + return antiSpamReplacementToken; + } + + public String plainMessage(AsyncChatEvent event) { + return PLAIN_TEXT_SERIALIZER.serialize(event.message()); + } + + public void setPlainMessage(AsyncChatEvent event, String message) { + event.message(Component.text(message)); + } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/Manager.java b/src/main/java/a4/papers/chatfilter/chatfilter/Manager.java index 2124928..87547cc 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/Manager.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/Manager.java @@ -1,17 +1,15 @@ package a4.papers.chatfilter.chatfilter; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.Plugin; - -import java.awt.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; + public class Manager { ChatFilter chatFilter; + private static final Pattern HEX_PATTERN = Pattern.compile("&#" + "([A-Fa-f0-9]{6})"); public Manager(ChatFilter instance) { chatFilter = instance; @@ -33,17 +31,19 @@ public boolean supported(String string) { } public static String colorStringHex(String msg) { - final Pattern hexPattern = Pattern.compile("&#" + "([A-Fa-f0-9]{6})"); - Matcher matcher = hexPattern.matcher(msg); - StringBuffer buffer = new StringBuffer(msg.length() + 4 * 8); + Matcher matcher = HEX_PATTERN.matcher(msg); + StringBuilder buffer = new StringBuilder(msg.length() + 4 * 8); + int lastEnd = 0; while (matcher.find()) { + buffer.append(msg, lastEnd, matcher.start()); String group = matcher.group(1); - matcher.appendReplacement(buffer, COLOR_CHAR + "x" - + COLOR_CHAR + group.charAt(0) + COLOR_CHAR + group.charAt(1) - + COLOR_CHAR + group.charAt(2) + COLOR_CHAR + group.charAt(3) - + COLOR_CHAR + group.charAt(4) + COLOR_CHAR + group.charAt(5) - ); + buffer.append(COLOR_CHAR).append("x") + .append(COLOR_CHAR).append(group.charAt(0)).append(COLOR_CHAR).append(group.charAt(1)) + .append(COLOR_CHAR).append(group.charAt(2)).append(COLOR_CHAR).append(group.charAt(3)) + .append(COLOR_CHAR).append(group.charAt(4)).append(COLOR_CHAR).append(group.charAt(5)); + lastEnd = matcher.end(); } - return ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString()); + buffer.append(msg.substring(lastEnd)); + return ChatColor.translateAlternateColorCodes('&', buffer.toString()); } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/BlacklistCommand.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/BlacklistCommand.java index 6ed40fc..b07d9fd 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/BlacklistCommand.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/BlacklistCommand.java @@ -1,22 +1,23 @@ package a4.papers.chatfilter.chatfilter.commands; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; + import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; public class BlacklistCommand implements CommandExecutor { @@ -44,8 +45,8 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } if (args[2].equals("ip")) { List strlist = new ArrayList<>(); - for (String words : chatFilter.regexAdvert.keySet()) { - strlist.add(chatFilter.regexAdvert.get(words).getWord()); + for (FilterWrapper wrapper : chatFilter.regexAdvert.values()) { + strlist.add(wrapper.getWord()); } Collections.sort(strlist); sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_BLACKLIST_LIST_IP_2.s))); @@ -67,14 +68,16 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } if (args[2].equals("word")) { List strlist = new ArrayList<>(); - for (String stringlist : chatFilter.regexWords.keySet()) { - strlist.add(chatFilter.regexWords.get(stringlist).getWord()); + for (FilterWrapper wrapper : chatFilter.regexWords.values()) { + String word = wrapper.getWord(); + if (!strlist.contains(word)) { + strlist.add(word); + } } - List listWithoutDuplicates = strlist.stream().distinct().collect(Collectors.toList()); - Collections.sort(listWithoutDuplicates); + Collections.sort(strlist); if (chatFilter.manager.supported("text-component")) { ComponentBuilder message = new ComponentBuilder(""); - for (String words : listWithoutDuplicates) { + for (String words : strlist) { message.append(ChatColor.WHITE + " " + words + ", "); message.event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/cf blacklist remove word " + words)); message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_BLACKLIST_LIST_WORD_1.s).replace("%word%", words))))); @@ -110,7 +113,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } if (args[2].equals("word") && args.length > 3) { - String ArgsString = String.join(" ", args).toLowerCase().replaceAll("blacklist remove word ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String ArgsString = sb.toString().toLowerCase(); ConfigurationSection config = chatFilter.getWordConfig().getConfigurationSection("ChatFilter"); Set set = config.getKeys(false); if (!set.contains(ArgsString)) { @@ -153,7 +161,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_BLACKLIST_ADD_IP_ARG.s))); return true; } else if (args[2].equals("word") && args.length > 3) { - String argsString = String.join(" ", args).toLowerCase().replace("blacklist add word ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String argsString = sb.toString().toLowerCase(); if (matchStringAdd(argsString)) { sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_BLACKLIST_ADD_WORD_NO.s).replace("%word%", argsString))); } else { @@ -161,7 +174,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String chatFilter.getFilters().createWordFilter(argsString, sender.getName()); } } else if (args[2].equals("ip") && args.length > 3) { - String argsString = String.join(" ", args).toLowerCase().replaceAll("blacklist add ip ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String argsString = sb.toString().toLowerCase(); sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_BLACKLIST_ADD_IP_ADDED.s).replace("%ip%", argsString))); chatFilter.getFilters().createAdvertFilter(argsString, sender.getName()); return true; diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ClearChatCommand.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ClearChatCommand.java index 248bf9d..08f1f87 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ClearChatCommand.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ClearChatCommand.java @@ -2,7 +2,6 @@ import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; -import a4.papers.chatfilter.chatfilter.shared.lang.LangManager; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java index d3d094f..02cf08c 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java @@ -4,7 +4,6 @@ import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; public class CommandHandler { ChatFilter chatFilter; @@ -22,12 +21,8 @@ public void runCommand(Player p, String[] word, FilterWrapper filterWrapper) { p.sendMessage(chatFilter.colour(s.replace("", "").replace("%player%", p.getName()).replace("%item%", firstWord))); } if (s.contains("")) { - new BukkitRunnable() { - @Override - public void run() { - Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), s.replace("", "").replace("%player%", p.getName()).replace("%item%", firstWord)); - } - }.runTask(chatFilter); + String command = s.replace("", "").replace("%player%", p.getName()).replace("%item%", firstWord); + Bukkit.getScheduler().runTask(chatFilter, () -> Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command)); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ImportCommand.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ImportCommand.java index 1f5a626..a4ea9eb 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ImportCommand.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ImportCommand.java @@ -1,15 +1,16 @@ package a4.papers.chatfilter.chatfilter.commands; -import a4.papers.chatfilter.chatfilter.ChatFilter; -import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; public class ImportCommand implements CommandExecutor { @@ -32,18 +33,27 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } public void load(CommandSender sender) { - try { - File fileObj = new File(chatFilter.getDataFolder(), "data.txt"); - BufferedReader reader = new BufferedReader(new FileReader(fileObj)); - String l; - while ((l = reader.readLine()) != null) { - String word = l.replace(" ", ""); - chatFilter.getFilters().createWordFilter(word, "Imported by " + sender.getName()); - sender.sendMessage(word + ChatColor.GOLD + " Added to filter."); - } - reader.close(); - } catch (Throwable t) { + File fileObj = new File(chatFilter.getDataFolder(), "data.txt"); + if (!fileObj.exists()) { sender.sendMessage(ChatColor.RED + "data.txt file is not found"); + return; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(fileObj))) { + String line; + int count = 0; + String importedBy = "Imported by " + sender.getName(); + + while ((line = reader.readLine()) != null) { + String word = line.trim().toLowerCase(); + if (!word.isEmpty() && word.length() > 1) { + chatFilter.getFilters().createWordFilter(word, importedBy); + count++; + } + } + sender.sendMessage(ChatColor.GOLD + "Successfully imported " + count + " words to filter."); + } catch (Exception e) { + sender.sendMessage(ChatColor.RED + "Error reading data.txt: " + e.getMessage()); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ReloadCommand.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ReloadCommand.java index 0642a55..ccc9815 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/ReloadCommand.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/ReloadCommand.java @@ -24,11 +24,13 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String chatFilter.saveConfig(); chatFilter.byPassWords.clear(); chatFilter.byPassDNS.clear(); - chatFilter.regexWords.clear(); - chatFilter.regexAdvert.clear(); + chatFilter.wordRegexPattern.clear(); + chatFilter.advertRegexPattern.clear(); + chatFilter.unicodeBlacklist.clear(); + chatFilter.unicodeWhitelist.clear(); chatFilter.reloadConfigs(); - chatFilter.getFilters().loadAdvertFilter(); - chatFilter.getFilters().loadWordFilter(); + chatFilter.getFilters().reloadFilters(); + chatFilter.getFilters().loadUnicodeFilter(); chatFilter.byPassWords = chatFilter.getWhitelistConfig().getStringList("bypassWords"); chatFilter.byPassDNS = chatFilter.getWhitelistConfig().getStringList("bypassIP"); chatFilter.loadVariables(); diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/commands/WhitelistCommand.java b/src/main/java/a4/papers/chatfilter/chatfilter/commands/WhitelistCommand.java index 71b1ced..a40e1b0 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/WhitelistCommand.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/WhitelistCommand.java @@ -1,5 +1,12 @@ package a4.papers.chatfilter.chatfilter.commands; +import java.util.Collections; +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; import net.md_5.bungee.api.ChatColor; @@ -7,12 +14,6 @@ import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; - -import java.util.Collections; -import java.util.List; public class WhitelistCommand implements CommandExecutor { @@ -92,7 +93,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } if (args[2].equals("ip") && args.length > 3) { - String ArgsString = String.join(" ", args).toLowerCase().replaceAll("whitelist add ip ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String ArgsString = sb.toString().toLowerCase(); List list = chatFilter.getWhitelistConfig().getStringList("bypassIP"); if (list.contains(ArgsString)) { sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_WHITELIST_ADD_IP_NO.s).replace("%ip%", ArgsString))); @@ -107,7 +113,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } } if (args[2].equals("word") && args.length > 3) { - String ArgsString = String.join(" ", args).toLowerCase().replaceAll("whitelist add word ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String ArgsString = sb.toString().toLowerCase(); List list = chatFilter.getWhitelistConfig().getStringList("bypassWords"); if (list.contains(ArgsString)) { sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_WHITELIST_ADD_WORD_NO.s).replace("%word%", ArgsString))); @@ -141,7 +152,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.NO_PERMISSION.s))); return true; } - String ArgsString = String.join(" ", args).toLowerCase().replaceAll("whitelist remove word ", ""); + StringBuilder sb = new StringBuilder(); + for (int i = 3; i < args.length; i++) { + if (i > 3) sb.append(' '); + sb.append(args[i]); + } + String ArgsString = sb.toString().toLowerCase(); List list = chatFilter.getWhitelistConfig().getStringList("bypassWords"); if (!list.contains(ArgsString)) { sender.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.CMD_WHITELIST_REMOVE_WORD_NO.s).replace("%word%", ArgsString))); diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/AnvilListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/AnvilListener.java index bb00024..8f54058 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/AnvilListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/AnvilListener.java @@ -1,14 +1,11 @@ package a4.papers.chatfilter.chatfilter.events; -import a4.papers.chatfilter.chatfilter.ChatFilter; -import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; -import a4.papers.chatfilter.chatfilter.shared.Result; -import a4.papers.chatfilter.chatfilter.shared.Types; -import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; import org.bukkit.ChatColor; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; -import org.bukkit.event.*; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.Inventory; @@ -17,6 +14,12 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.EventExecutor; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.Result; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; + public class AnvilListener implements EventExecutor, Listener { ChatFilter chatFilter; @@ -30,12 +33,14 @@ public void execute(final Listener listener, final Event event) throws EventExce } public void onInventoryClick(InventoryClickEvent event) { - if (event.getWhoClicked().hasPermission("chatfilter.bypass") || event.getWhoClicked().hasPermission("chatfilter.bypass.anvil")) + HumanEntity ent = event.getWhoClicked(); + if (!(ent instanceof Player)) { + return; + } + Player p = (Player) ent; + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.anvil")) return; if (!event.isCancelled()) { - HumanEntity ent = event.getWhoClicked(); - if (ent instanceof Player) { - Player p = (Player) ent; Inventory inv = event.getInventory(); if (inv instanceof AnvilInventory) { InventoryView view = event.getView(); @@ -82,8 +87,9 @@ public void onInventoryClick(InventoryClickEvent event) { if (filterWrapper.getLogToConsole()) chatFilter.sendConsole(type, displayName, p, filterWrapper.getRegex(), "Anvil"); if (filterWrapper.getSendStaff()) { - for (String oneWord : chatFilter.getChatFilters().validResult(displayName, p).getStringArray()) { - displayName = displayName.replace(oneWord, chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + String highlightColored = chatFilter.colour(chatFilter.settingsSwearHighLight); + for (String oneWord : stringArray) { + displayName = displayName.replace(oneWord, highlightColored.replace("%catch%", oneWord)); } chatFilter.sendStaffMessage(chatFilter.colour(prefix + displayName)); } @@ -94,7 +100,6 @@ public void onInventoryClick(InventoryClickEvent event) { } } } - } } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java index a555dc3..93e9f5c 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java @@ -1,11 +1,11 @@ package a4.papers.chatfilter.chatfilter.events; -import a4.papers.chatfilter.chatfilter.ChatFilter; -import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; -import a4.papers.chatfilter.chatfilter.shared.Result; -import a4.papers.chatfilter.chatfilter.shared.Types; -import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; -import org.bukkit.ChatColor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -14,11 +14,12 @@ import org.bukkit.event.player.PlayerEditBookEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.EventExecutor; -import org.bukkit.scheduler.BukkitRunnable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.Result; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; public class BooksListener implements EventExecutor, Listener { ChatFilter chatFilter; @@ -33,9 +34,9 @@ public void execute(final Listener listener, final Event event) throws EventExce public void onBookEvent(PlayerEditBookEvent event) { Player p = event.getPlayer(); - if (event.getPlayer().hasPermission("chatfilter.bypass") || event.getPlayer().hasPermission("chatfilter.bypass.book")) + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.book")) return; - List catchMatch = new ArrayList<>(); + Set catchMatch = new LinkedHashSet<>(); List bookPageMatch = new ArrayList<>(); List bookPagesList = event.getNewBookMeta().getPages(); String prefix = "Error"; @@ -45,52 +46,48 @@ public void onBookEvent(PlayerEditBookEvent event) { boolean resulted = false; int nom = 0; String[] stringArray; - for (String pageFilter : bookPagesList) { + for (int i = 0; i < bookPagesList.size(); i++) { + String pageFilter = bookPagesList.get(i); Result result = chatFilter.getChatFilters().validResult(pageFilter, p); + if (!result.getResult()) { + continue; + } type = result.getType(); stringArray = result.getStringArray(); filterWrapper = result.getFilterWrapper(); chatFilter.commandHandler.runCommand(p, stringArray, filterWrapper); - nom = bookPagesList.indexOf(pageFilter) + 1; - if (result.getResult()) { - resulted = true; - switch (type) { - case SWEAR: - bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookSwear.s).replace("%player%", p.getName()); - break; - case IP_DNS: - bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookIP.s).replace("%player%", p.getName()); - break; - case IP_SWEAR: - bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearAndIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookIPandSwear.s).replace("%player%", p.getName()); - break; - case FONT: - bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); - warnPlayerMessage = chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.warnFontMessage.s)); - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookFont.s).replace("%player%", p.getName()); - break; - default: - throw new IllegalStateException("Unexpected value: " + type); - } - catchMatch.addAll(Arrays.asList(stringArray)); + nom = i + 1; + resulted = true; + switch (type) { + case SWEAR: + bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookSwear.s).replace("%player%", p.getName()); + break; + case IP_DNS: + bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookIP.s).replace("%player%", p.getName()); + break; + case IP_SWEAR: + bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearAndIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookIPandSwear.s).replace("%player%", p.getName()); + break; + case FONT: + bookPageMatch.add(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.bookPage.s)).replace("%num%", nom+"") + pageFilter); + warnPlayerMessage = chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.warnFontMessage.s)); + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixBookFont.s).replace("%player%", p.getName()); + break; + default: + throw new IllegalStateException("Unexpected value: " + type); } + catchMatch.addAll(Arrays.asList(stringArray)); } if (resulted) { - if (event.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.WRITABLE_BOOK)) { - event.getPlayer().getInventory().getItemInMainHand().setAmount(event.getPlayer().getInventory().getItemInMainHand().getAmount() - 1); - - new BukkitRunnable() { - @Override - public void run() { - event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.WRITABLE_BOOK, 1)); - } - }.runTaskLater(chatFilter, 1); + if (p.getInventory().getItemInMainHand().getType().equals(Material.WRITABLE_BOOK)) { + p.getInventory().getItemInMainHand().setAmount(p.getInventory().getItemInMainHand().getAmount() - 1); + ChatFilter.runTaskLater(() -> p.getInventory().setItemInMainHand(new ItemStack(Material.WRITABLE_BOOK, 1)), 1L); } if (filterWrapper.getLogToConsole()) chatFilter.sendConsole(type, bookPagesList.get(nom - 1), p, filterWrapper.getRegex(), "Book"); @@ -99,15 +96,14 @@ public void run() { } if (filterWrapper.getSendStaff()) { chatFilter.sendStaffMessage(chatFilter.colour(prefix)); + String highlightColored = chatFilter.colour(chatFilter.settingsSwearHighLight); for (String page : bookPageMatch) { for (String oneWord : catchMatch) { - page = page.replace(oneWord, chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + page = page.replace(oneWord, highlightColored.replace("%catch%", oneWord)); } chatFilter.sendStaffMessage(page); } } - bookPageMatch.clear(); - catchMatch.clear(); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/CapsChatListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/CapsChatListener.java index 755588b..c8350b5 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/CapsChatListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/CapsChatListener.java @@ -1,14 +1,14 @@ package a4.papers.chatfilter.chatfilter.events; -import a4.papers.chatfilter.chatfilter.ChatFilter; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.event.*; -import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; import org.bukkit.plugin.EventExecutor; +import io.papermc.paper.event.player.AsyncChatEvent; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import a4.papers.chatfilter.chatfilter.ChatFilter; public class CapsChatListener implements EventExecutor, Listener { @@ -20,13 +20,14 @@ public CapsChatListener(ChatFilter instance) { @Override public void execute(final Listener listener, final Event event) throws EventException { - this.onPlayerCaps((AsyncPlayerChatEvent) event); + this.onPlayerCaps((AsyncChatEvent) event); } - public void onPlayerCaps(AsyncPlayerChatEvent event) { - String msg = event.getMessage(); + public void onPlayerCaps(AsyncChatEvent event) { + String msg = chatFilter.plainMessage(event); if (chatFilter.deCap) { - if (event.getPlayer().hasPermission("chatfilter.bypass") || event.getPlayer().hasPermission("chatfilter.bypass.caps")) + Player player = event.getPlayer(); + if (player.isOp() || player.hasPermission("chatfilter.bypass") || player.hasPermission("chatfilter.bypass.caps")) return; if (isURL(msg)) return; @@ -39,24 +40,18 @@ public void onPlayerCaps(AsyncPlayerChatEvent event) { msg = msg.charAt(0) + msg.substring(1).toLowerCase(); } String newmsg = msg; - for (Player p : Bukkit.getOnlinePlayers()) { - String player = p.getName(); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + String playerName = onlinePlayer.getName(); - if (msg.toLowerCase().contains(player.toLowerCase())) { - newmsg = newmsg.replace(player.toLowerCase(), player); + if (msg.toLowerCase().contains(playerName.toLowerCase())) { + newmsg = newmsg.replace(playerName.toLowerCase(), playerName); } } - event.setMessage(newmsg); + chatFilter.setPlainMessage(event, newmsg); } } public boolean isURL(String str) { - boolean matched = false; - Pattern p = Pattern.compile(chatFilter.URL_REGEX); - Matcher m = p.matcher(str); - if (m.find()) { - matched = true; - } - return matched; + return chatFilter.urlPattern.matcher(str).find(); } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/ChatDelayListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/ChatDelayListener.java index 5d898f4..529f1ad 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/ChatDelayListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/ChatDelayListener.java @@ -1,6 +1,5 @@ package a4.papers.chatfilter.chatfilter.events; - import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.ChatData; import a4.papers.chatfilter.chatfilter.shared.StringSimilarity; @@ -10,66 +9,101 @@ import org.bukkit.event.EventException; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.EventExecutor; +import io.papermc.paper.event.player.AsyncChatEvent; import java.math.BigDecimal; -import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; public class ChatDelayListener implements EventExecutor, Listener { - public Map chatmsgs = new HashMap(); + private static final int CLEANUP_INTERVAL = 256; + static { + if ((CLEANUP_INTERVAL & (CLEANUP_INTERVAL - 1)) != 0) { + throw new IllegalStateException("CLEANUP_INTERVAL must be a power of 2"); + } + } + public final Map chatmsgs = new ConcurrentHashMap<>(); ChatFilter chatFilter; - private HashMap chatMSG = new HashMap<>(); - private HashMap cooldown = new HashMap<>(); + private Double similarityThreshold; + private int cleanupCounter; public ChatDelayListener(ChatFilter instance) { chatFilter = instance; } + + private double getSimilarityThreshold() { + if (similarityThreshold == null) { + String percent = chatFilter.percentage.trim().replace("%", ""); + similarityThreshold = new BigDecimal(percent).divide(BigDecimal.valueOf(100)).doubleValue(); + } + return similarityThreshold; + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + chatmsgs.remove(event.getPlayer().getUniqueId()); + } + + private void pruneExpiredEntries(long now) { + Iterator> iterator = chatmsgs.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getValue().getLong() <= now) { + iterator.remove(); + } + } + } @Override public void execute(final Listener listener, final Event event) throws EventException { - this.onPlayerSpam((AsyncPlayerChatEvent) event); + this.onPlayerSpam((AsyncChatEvent) event); } @EventHandler - public void onPlayerSpam(AsyncPlayerChatEvent e) { + public void onPlayerSpam(AsyncChatEvent e) { if (!chatFilter.antiRepeatEnabled) { return; } - if (e.getPlayer().hasPermission("chatfilter.bypass") || e.getPlayer().hasPermission("chatfilter.bypass.repeat")) - return; + Player p = e.getPlayer(); + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.repeat")) { + return; + } + UUID playerUUID = p.getUniqueId(); - String msg = e.getMessage(); - chatmsgs.containsKey(playerUUID); + String msg = chatFilter.plainMessage(e); + long currentTime = System.currentTimeMillis(); long configtime = chatFilter.repeatDelay * 1000L; - if (!p.hasPermission("chatfilter.bypass") || !e.getPlayer().hasPermission("chatfilter.bypass.repeat") && chatFilter.antiRepeatEnabled) { - if (chatmsgs.containsKey(playerUUID)) { - long time = chatmsgs.get(playerUUID).getLong(); - double sim = StringSimilarity.similarity(msg, chatmsgs.get(playerUUID).getString()); - BigDecimal d = new BigDecimal(chatFilter.percentage.trim().replace("%", "")).divide(BigDecimal.valueOf(100)); - if (sim > d.doubleValue()) { - if (time > System.currentTimeMillis()) { - e.setCancelled(true); - int remainingTime = Math.round((this.chatmsgs.get(playerUUID).getLong() - System.currentTimeMillis()) / 1000 * 10) / 10; - String timeString = ""; - if (remainingTime >= 2) { - timeString = remainingTime + " seconds"; - } else { - timeString = 1 + " second"; - } - p.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.chatRepeatMessage.s).replace("%time%", timeString))); - } else { - chatmsgs.put(playerUUID, new ChatData(msg, System.currentTimeMillis() + configtime)); - } - } else { - chatmsgs.remove(playerUUID); - } + + if ((++cleanupCounter & (CLEANUP_INTERVAL - 1)) == 0) { + pruneExpiredEntries(currentTime); + } + + ChatData chatData = chatmsgs.get(playerUUID); + if (chatData == null) { + chatmsgs.put(playerUUID, new ChatData(msg, currentTime + configtime)); + return; + } + + long expiryTime = chatData.getLong(); + double sim = StringSimilarity.similarity(msg, chatData.getString()); + + if (sim > getSimilarityThreshold()) { + if (expiryTime > currentTime) { + e.setCancelled(true); + long remainingMs = expiryTime - currentTime; + int remainingTime = (int) Math.ceil(remainingMs / 1000.0); + String timeString = remainingTime >= 2 ? remainingTime + " seconds" : "1 second"; + p.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.chatRepeatMessage.s).replace("%time%", timeString))); } else { - chatmsgs.put(playerUUID, new ChatData(msg, System.currentTimeMillis() + configtime)); + chatmsgs.put(playerUUID, new ChatData(msg, currentTime + configtime)); } + } else { + chatmsgs.put(playerUUID, new ChatData(msg, currentTime + configtime)); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/CommandListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/CommandListener.java index ee72395..b65e07c 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/CommandListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/CommandListener.java @@ -1,21 +1,19 @@ package a4.papers.chatfilter.chatfilter.events; - -import a4.papers.chatfilter.chatfilter.ChatFilter; -import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; -import a4.papers.chatfilter.chatfilter.shared.Result; -import a4.papers.chatfilter.chatfilter.shared.Types; -import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventException; -import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.plugin.EventExecutor; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.Result; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; + public class CommandListener implements EventExecutor, Listener { ChatFilter chatFilter; @@ -30,17 +28,19 @@ public void execute(final Listener listener, final Event event) throws EventExce public void onPlayerCommand(PlayerCommandPreprocessEvent event) { Player p = event.getPlayer(); - String cmd = ChatColor.stripColor(event.getMessage().toLowerCase()); - String[] array = cmd.split(" "); - if (p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.command")) - return; - if (event.isCancelled()) + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.command")) return; - if (!chatFilter.cmdCheck) + if (event.isCancelled() || !chatFilter.cmdCheck) return; - if (chatFilter.getConfig().getConfigurationSection("commands").getKeys(false).contains(array[0].replace("/", ""))) { - boolean swearconfig = chatFilter.getConfig().getBoolean("commands." + array[0].replace("/", "") + ".swear"); - boolean dnsconfig = chatFilter.getConfig().getBoolean("commands." + array[0].replace("/", "") + ".ip"); + + String cmd = ChatColor.stripColor(event.getMessage().toLowerCase()); + String[] array = cmd.split(" "); + String commandName = array[0].replace("/", ""); + + if (chatFilter.getConfig().getConfigurationSection("commands").getKeys(false).contains(commandName)) { + String configPath = "commands." + commandName; + boolean swearconfig = chatFilter.getConfig().getBoolean(configPath + ".swear"); + boolean dnsconfig = chatFilter.getConfig().getBoolean(configPath + ".ip"); String prefix = "Error"; String warnPlayerMessage = "Error"; @@ -49,31 +49,30 @@ public void onPlayerCommand(PlayerCommandPreprocessEvent event) { Types type = result.getType(); String[] stringArray = result.getStringArray(); FilterWrapper filterWrapper = result.getFilterWrapper(); - if (type == Types.SWEAR && !swearconfig) { - return; - } - if (type == Types.IP_DNS && !dnsconfig) { + if ((type == Types.SWEAR && !swearconfig) || (type == Types.IP_DNS && !dnsconfig)) { return; } + + String placeHolder = chatFilter.getLang().stringArrayToString(stringArray); + String playerName = p.getName(); + if (type == Types.SWEAR && swearconfig) { - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdSwear.s).replace("%player%", p.getName()); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - } - if (type == Types.IP_DNS && dnsconfig) { - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIP.s).replace("%player%", p.getName()); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - } - if (type == Types.IP_SWEAR && dnsconfig && !swearconfig) { - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIP.s).replace("%player%", p.getName()); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - } - if (type == Types.IP_SWEAR && !dnsconfig && swearconfig) { - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdSwear.s).replace("%player%", p.getName()); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); - } - if (type == Types.IP_SWEAR && dnsconfig && swearconfig) { - prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIPandSwear.s).replace("%player%", p.getName()); - warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearAndIPMessage.s).replace("%placeHolder%", (chatFilter.getLang().stringArrayToString(stringArray))); + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdSwear.s).replace("%player%", playerName); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", placeHolder); + } else if (type == Types.IP_DNS && dnsconfig) { + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIP.s).replace("%player%", playerName); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", placeHolder); + } else if (type == Types.IP_SWEAR) { + if (dnsconfig && swearconfig) { + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIPandSwear.s).replace("%player%", playerName); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearAndIPMessage.s).replace("%placeHolder%", placeHolder); + } else if (dnsconfig) { + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdIP.s).replace("%player%", playerName); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnIPMessage.s).replace("%placeHolder%", placeHolder); + } else if (swearconfig) { + prefix = chatFilter.getLang().mapToString(EnumStrings.prefixCmdSwear.s).replace("%player%", playerName); + warnPlayerMessage = chatFilter.getLang().mapToString(EnumStrings.warnSwearMessage.s).replace("%placeHolder%", placeHolder); + } } if (filterWrapper.getCancelChat()) { event.setCancelled(true); @@ -94,8 +93,9 @@ public void onPlayerCommand(PlayerCommandPreprocessEvent event) { p.sendMessage(chatFilter.colour(warnPlayerMessage)); } if (filterWrapper.getSendStaff()) { + String highlightColored = chatFilter.colour(chatFilter.settingsSwearHighLight); for (String oneWord : stringArray) { - cmd = cmd.replace(oneWord, chatFilter.colour( chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%",oneWord)))); + cmd = cmd.replace(oneWord, highlightColored.replace("%catch%", oneWord)); } chatFilter.sendStaffMessage(chatFilter.colour(prefix + cmd)); } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/PauseChat.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/PauseChat.java index c49df61..dd3bcf1 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/PauseChat.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/PauseChat.java @@ -1,11 +1,16 @@ package a4.papers.chatfilter.chatfilter.events; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import io.papermc.paper.event.player.AsyncChatEvent; import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; -import org.bukkit.event.*; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.plugin.EventExecutor; public class PauseChat implements EventExecutor, Listener { ChatFilter chatFilter; @@ -15,23 +20,22 @@ public PauseChat(ChatFilter instance) { } @Override public void execute(final Listener listener, final Event event) throws EventException { - this.onPlayerChatPause((AsyncPlayerChatEvent) event); + this.onPlayerChatPause((AsyncChatEvent) event); } @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerChatPause(AsyncPlayerChatEvent event) { - if (event.isCancelled()) + public void onPlayerChatPause(AsyncChatEvent event) { + if (event.isCancelled() || !chatFilter.chatPause) + return; + + Player player = event.getPlayer(); + if (player.isOp() || player.hasPermission("chatfilter.bypass") || player.hasPermission("chatfilter.pause") || player.hasPermission("chatfilter.bypass.pause")) { return; - if (chatFilter.chatPause) { - if (event.getPlayer().hasPermission("chatfilter.bypass") || event.getPlayer().hasPermission("chatfilter.pause") || event.getPlayer().hasPermission("chatfilter.bypass.pause")) { - } else { - event.setCancelled(true); - event.getPlayer().sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.denyMessagePause.s))); - chatFilter.logMsg("[Chat filter] (Paused chat) " + event.getPlayer().getDisplayName() + ": " + event.getMessage()); - } - } - + + event.setCancelled(true); + player.sendMessage(chatFilter.colour(chatFilter.getLang().mapToString(EnumStrings.denyMessagePause.s))); + chatFilter.logMsg("[Chat filter] (Paused chat) " + player.getDisplayName() + ": " + chatFilter.plainMessage(event)); } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/RepeatCharListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/RepeatCharListener.java index af98cd0..53d8c1b 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/RepeatCharListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/RepeatCharListener.java @@ -1,14 +1,12 @@ package a4.papers.chatfilter.chatfilter.events; -import a4.papers.chatfilter.chatfilter.ChatFilter; import org.bukkit.event.Event; import org.bukkit.event.EventException; import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.plugin.EventExecutor; +import io.papermc.paper.event.player.AsyncChatEvent; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import a4.papers.chatfilter.chatfilter.ChatFilter; public class RepeatCharListener implements EventExecutor, Listener { @@ -20,29 +18,24 @@ public RepeatCharListener(ChatFilter instance) { @Override public void execute(final Listener listener, final Event event) throws EventException { - this.onPlayerCarSpam((AsyncPlayerChatEvent) event); + this.onPlayerCarSpam((AsyncChatEvent) event); } - public void onPlayerCarSpam(AsyncPlayerChatEvent event) { - String msg = event.getMessage(); + public void onPlayerCarSpam(AsyncChatEvent event) { + String msg = chatFilter.plainMessage(event); if (chatFilter.antiSpamEnabled) { - if (event.getPlayer().hasPermission("chatfilter.bypass") || event.getPlayer().hasPermission("chatfilter.bypass.characters")) { + org.bukkit.entity.Player player = event.getPlayer(); + if (player.isOp() || player.hasPermission("chatfilter.bypass") || player.hasPermission("chatfilter.bypass.characters")) { return; } if (isURL(msg)) { return; } - event.setMessage(chatFilter.antiSpamPattern.matcher(msg).replaceAll(new String(new char[chatFilter.antiSpamReplaceAmount]).replace("\0", "$1"))); + chatFilter.setPlainMessage(event, chatFilter.antiSpamPattern.matcher(msg).replaceAll(chatFilter.getAntiSpamReplacementToken())); } } public boolean isURL(String str) { - boolean matched = false; - Pattern p = Pattern.compile(chatFilter.URL_REGEX); - Matcher m = p.matcher(str); - if (m.find()) { - matched = true; - } - return matched; + return chatFilter.urlPattern.matcher(str).find(); } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/SignListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/SignListener.java index 3358755..930ff56 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/SignListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/SignListener.java @@ -1,11 +1,5 @@ package a4.papers.chatfilter.chatfilter.events; -import a4.papers.chatfilter.chatfilter.ChatFilter; -import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; -import a4.papers.chatfilter.chatfilter.shared.Result; -import a4.papers.chatfilter.chatfilter.shared.Types; -import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -14,7 +8,12 @@ import org.bukkit.event.block.SignChangeEvent; import org.bukkit.plugin.EventExecutor; -import java.util.List; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.Result; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; + public class SignListener implements EventExecutor, Listener { @@ -31,7 +30,7 @@ public void execute(final Listener listener, final Event event) throws EventExce public void onSignEvent(SignChangeEvent event) { Player p = event.getPlayer(); - if (p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.sign")) { + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.sign")) { return; } boolean broken = false; @@ -86,9 +85,10 @@ public void onSignEvent(SignChangeEvent event) { if (filterWrapper.getSendStaff()) { chatFilter.sendStaffMessage(chatFilter.colour(prefix)); } + String highlightColored = chatFilter.colour(chatFilter.settingsSwearHighLight); if (!event.getLine(0).isEmpty()) { for (String oneWord : stringArray) { - line0 = line0.replace(oneWord.replace(" ", ""), chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + line0 = line0.replace(oneWord.replace(" ", ""), highlightColored.replace("%catch%", oneWord)); line0Sign = line0Sign.replace(oneWord, filterWrapper.getReplace()); } if (!broken && filterWrapper.getCancelChatReplace()) @@ -100,7 +100,7 @@ public void onSignEvent(SignChangeEvent event) { if (!event.getLine(1).isEmpty()) { for (String oneWord : stringArray) { - line1 = line1.replace(oneWord.replace(" ", ""), chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + line1 = line1.replace(oneWord.replace(" ", ""), highlightColored.replace("%catch%", oneWord)); line1Sign = line1Sign.replace(oneWord, filterWrapper.getReplace()); } if (!broken && filterWrapper.getCancelChatReplace()) @@ -111,7 +111,7 @@ public void onSignEvent(SignChangeEvent event) { } if (!event.getLine(2).isEmpty()) { for (String oneWord : stringArray) { - line2 = line2.replace(oneWord.replace(" ", ""), chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + line2 = line2.replace(oneWord.replace(" ", ""), highlightColored.replace("%catch%", oneWord)); line2Sign = line2Sign.replace(oneWord, filterWrapper.getReplace()); } if (!broken && filterWrapper.getCancelChatReplace()) @@ -122,7 +122,7 @@ public void onSignEvent(SignChangeEvent event) { } if (!event.getLine(3).isEmpty()) { for (String oneWord : stringArray) { - line3 = line3.replace(oneWord.replace(" ", ""), chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + line3 = line3.replace(oneWord.replace(" ", ""), highlightColored.replace("%catch%", oneWord)); line3Sign = line3Sign.replace(oneWord, filterWrapper.getReplace()); } if (!broken && filterWrapper.getCancelChatReplace()) diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java b/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java index f4e1071..5e49efd 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java @@ -1,5 +1,12 @@ package a4.papers.chatfilter.chatfilter.events; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import io.papermc.paper.event.player.AsyncChatEvent; import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; @@ -7,14 +14,8 @@ import a4.papers.chatfilter.chatfilter.shared.Result; import a4.papers.chatfilter.chatfilter.shared.Types; import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.EventException; -import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.plugin.EventExecutor; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; public class SwearChatListener implements EventExecutor, Listener { @@ -26,21 +27,37 @@ public SwearChatListener(ChatFilter instance) { @Override public void execute(final Listener listener, final Event event) throws EventException { - this.onPlayerSwear((AsyncPlayerChatEvent) event); + this.onPlayerSwear((AsyncChatEvent) event); } - public void onPlayerSwear(AsyncPlayerChatEvent event) { + public void onPlayerSwear(AsyncChatEvent event) { Player p = event.getPlayer(); - String chatMessage = ChatColor.stripColor(event.getMessage()).toLowerCase(); + String rawMessage = chatFilter.plainMessage(event); + String chatMessage = ChatColor.stripColor(rawMessage).toLowerCase(); String prefix = ""; String warnPlayerMessage = ""; - if (p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.chat")) + if (p.isOp() || p.hasPermission("chatfilter.bypass") || p.hasPermission("chatfilter.bypass.chat")) return; if (event.isCancelled()) return; if (chatFilter.chatPause) return; + // Early check for non-English letters if enabled (after bypass/pause checks) + if (chatFilter.settingsBlockCustomSybols) { + String strippedRawMessage = ChatColor.stripColor(rawMessage); + if (chatFilter.getChatFilters().containsNonEnglishLetters(strippedRawMessage)) { + String deny = "&cYour message was not sent due to containing disallowed characters."; + p.sendMessage(chatFilter.colour(deny)); + try { + p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(chatFilter.colour(deny))); + } catch (Throwable ignored) { + // Fallback silently if actionbar is unavailable + } + event.setCancelled(true); + return; + } + } Result result = chatFilter.getChatFilters().validResult(chatMessage, p); if (result.getResult()) { Types type = result.getType(); @@ -76,21 +93,22 @@ public void onPlayerSwear(AsyncPlayerChatEvent event) { if (filterWrapper.getWarnPlayer()) p.sendMessage(chatFilter.colour(warnPlayerMessage)); if (filterWrapper.getSendStaff()) { + String highlightColored = chatFilter.colour(chatFilter.settingsSwearHighLight); for (String oneWord : stringArray) { - chatMessage = chatMessage.replace(oneWord, chatFilter.colour(chatFilter.settingsSwearHighLight.replace("%catch%", oneWord))); + chatMessage = chatMessage.replace(oneWord, highlightColored.replace("%catch%", oneWord)); } chatFilter.sendStaffMessage(chatFilter.colour(prefix + chatMessage)); } if (filterWrapper.getCancelChat()) { event.setCancelled(true); } else { - String msg = event.getMessage(); + String msg = rawMessage; for (String oneWord : stringArray) { if (filterWrapper.getCancelChatReplace()) { msg = LowerCaseReplace.replace(msg, oneWord, filterWrapper.getReplace()); } } - event.setMessage(msg); + chatFilter.setPlainMessage(event, msg); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java index 26f4595..8b013fc 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java @@ -1,15 +1,22 @@ package a4.papers.chatfilter.chatfilter.shared; -import a4.papers.chatfilter.chatfilter.ChatFilter; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.entity.Player; + +import a4.papers.chatfilter.chatfilter.ChatFilter; + public class ChatFilters { + private static final FilterWrapper URL_FILTER = new FilterWrapper("URL", Collections.singletonList("none"), "URL", true, false, "", false, true, false); + private static final FilterWrapper FONT_FILTER = new FilterWrapper("unicode", Collections.singletonList("none"), "unicode", true, false, "", true, true, true); + ChatFilter chatFilter; public ChatFilters(ChatFilter instance) { @@ -17,118 +24,171 @@ public ChatFilters(ChatFilter instance) { } private String removeBypass(String s) { - List bypassItems = new ArrayList(chatFilter.byPassWords); - bypassItems.addAll(chatFilter.byPassDNS); - for (String removewording : bypassItems) { + for (String removewording : chatFilter.byPassWords) { + if (s.contains(removewording)) { + s = s.replace(removewording, " "); + } + } + for (String removewording : chatFilter.byPassDNS) { if (s.contains(removewording)) { - s = s.replaceAll(removewording, " "); + s = s.replace(removewording, " "); } } return s; } public Result validResult(String string, Player player) { + String lowercaseString = removeBypass(string.toLowerCase()); + boolean matched = false; boolean matchedSwear = false; boolean matchedIP = false; boolean matchedURL = false; - String regex = ""; - Map regexMap = new HashMap<>(); - String lowercaseString = removeBypass(string.toLowerCase()); - Types type = Types.NOTYPE; - List groupWords = new ArrayList(); - List regexUsed = new ArrayList(); - if (!(player.hasPermission("chatfilter.bypass.swear"))) { + String regex = null; + FilterWrapper matchedWrapper = null; + boolean isOp = player.isOp(); + boolean canBypassSwear = isOp || player.hasPermission("chatfilter.bypass.swear"); + boolean canBypassIP = isOp || player.hasPermission("chatfilter.bypass.ip"); + boolean canBypassURL = isOp || player.hasPermission("chatfilter.bypass.url"); + + Set groupWords = new LinkedHashSet<>(); + List regexUsed = new ArrayList<>(); + + // Check swear words + if (!canBypassSwear && !chatFilter.wordRegexPattern.isEmpty()) { for (Pattern p : chatFilter.wordRegexPattern) { Matcher m = p.matcher(lowercaseString); while (m.find()) { - if (!player.hasPermission("chatfilter.bypass.swear." + m.group(0))) + String match = m.group(0); + if (!player.hasPermission("chatfilter.bypass.swear." + match)) { matched = true; - matchedSwear = true; - regex = p.pattern(); - regexUsed.add(p.pattern()); - if (!groupWords.contains(m.group(0))) { - groupWords.add(m.group(0)); + matchedSwear = true; + regex = p.pattern(); + matchedWrapper = chatFilter.regexWords.get(regex); + regexUsed.add(regex); + groupWords.add(match); } } } } - if (!(player.hasPermission("chatfilter.bypass.ip"))) { + if (!canBypassIP && !chatFilter.advertRegexPattern.isEmpty()) { for (Pattern p : chatFilter.advertRegexPattern) { Matcher m = p.matcher(lowercaseString); while (m.find()) { - if (!player.hasPermission("chatfilter.bypass.ip." + m.group(0))) + String match = m.group(0); + if (!player.hasPermission("chatfilter.bypass.ip." + match)) { matched = true; - matchedIP = true; - regex = p.pattern(); - if (!groupWords.contains(m.group(0))) { - groupWords.add(m.group(0)); + matchedIP = true; + regex = p.pattern(); + matchedWrapper = chatFilter.regexAdvert.get(regex); + groupWords.add(match); } } } } - - if (!(player.hasPermission("chatfilter.bypass.url"))) { - if (!chatFilter.settingsAllowURL) { - Pattern p = Pattern.compile(chatFilter.URL_REGEX); - Matcher m = p.matcher(lowercaseString); - if (m.find()) { - matched = true; - matchedURL = true; - regex = chatFilter.URL_REGEX; + + // Check URL + if (!canBypassURL && !chatFilter.settingsAllowURL) { + Matcher m = chatFilter.urlPattern.matcher(lowercaseString); + if (m.find()) { + matched = true; + matchedURL = true; + regex = chatFilter.URL_REGEX; + if (matchedWrapper == null) { + matchedWrapper = URL_FILTER; } - regexMap.put(chatFilter.URL_REGEX, new FilterWrapper("URL", Collections.singletonList("none"), chatFilter.URL_REGEX, true, false, "", false, true, false)); } } - if (matchedURL) { - matched = true; - type = Types.URL; - } + // Check font if (isFont(string)) { matched = true; - type = Types.FONT; regex = "unicode"; - regexMap.put("unicode", new FilterWrapper("unicode", Collections.singletonList("none"), "unicode", true, false, "", true, true, true)); - - } - if (matchedSwear) { - type = Types.SWEAR; - } - if (matchedIP) { - type = Types.IP_DNS; + if (matchedWrapper == null) { + matchedWrapper = FONT_FILTER; + } } + + // Determine type + Types type; if (matchedSwear && matchedIP) { type = Types.IP_SWEAR; + } else if (matchedSwear) { + type = Types.SWEAR; + } else if (matchedIP) { + type = Types.IP_DNS; + } else if (matchedURL) { + type = Types.URL; + } else if (matched) { + type = Types.FONT; + } else { + type = Types.NOTYPE; } - String[] array = new String[groupWords.size()]; - groupWords.toArray(array); - regexMap.putAll(chatFilter.regexWords); - regexMap.putAll(chatFilter.regexAdvert); - return new Result(matched, array, type, regexMap.get(regex), regexUsed); + String[] array = groupWords.toArray(new String[0]); + if (matchedWrapper == null && regex != null) { + matchedWrapper = chatFilter.regexWords.get(regex); + if (matchedWrapper == null) { + matchedWrapper = chatFilter.regexAdvert.get(regex); + } + } + return new Result(matched, array, type, matchedWrapper, regexUsed); } public boolean isFont(String string) { - boolean matchedFont = false; + if (!chatFilter.settingsBlockFancyChat || chatFilter.unicodeBlacklist.isEmpty()) { + return false; + } + + // Remove whitelisted characters first + String processedString = string; for (String s : chatFilter.unicodeWhitelist) { - if (string.contains(s)) { - string = string.replace(s, ""); + if (processedString.contains(s)) { + processedString = processedString.replace(s, ""); } } - if (chatFilter.settingsBlockFancyChat) { - for (String s : chatFilter.unicodeBlacklist.keySet()) { - int UrangeLow = Integer.parseInt(chatFilter.unicodeBlacklist.get(s).getStart(), 16); - int UrangeHigh = Integer.parseInt(chatFilter.unicodeBlacklist.get(s).getEnd(), 16); - for (int iLetter = 0; iLetter < string.length(); iLetter++) { - int cp = string.codePointAt(iLetter); - if (cp >= UrangeLow && cp <= UrangeHigh) { - matchedFont = true; - } + + // Early exit if string is now empty + if (processedString.isEmpty()) { + return false; + } + + // Check unicode ranges + for (UnicodeWrapper wrapper : chatFilter.unicodeBlacklist.values()) { + int urangeLow = wrapper.getStartInt(); + int urangeHigh = wrapper.getEndInt(); + + int length = processedString.length(); + for (int i = 0; i < length; ) { + int cp = processedString.codePointAt(i); + if (cp >= urangeLow && cp <= urangeHigh) { + return true; + } + i += Character.charCount(cp); + } + } + return false; + } + + public boolean containsNonEnglishLetters(String string) { + if (!chatFilter.settingsBlockCustomSybols) { + return false; + } + if (string == null || string.isEmpty()) { + return false; + } + int length = string.length(); + for (int i = 0; i < length;) { + int cp = string.codePointAt(i); + // Only consider letters; allow digits, symbols, whitespace + if (Character.isLetter(cp)) { + if (!((cp >= 'A' && cp <= 'Z') || (cp >= 'a' && cp <= 'z'))) { + return true; } } + i += Character.charCount(cp); } - return matchedFont; + return false; } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/LowerCaseReplace.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/LowerCaseReplace.java index 31cb0e9..6230a8f 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/LowerCaseReplace.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/LowerCaseReplace.java @@ -4,20 +4,29 @@ public class LowerCaseReplace { public static String replace(String source, String target, String replacement) { - StringBuilder sbSource = new StringBuilder(source); - StringBuilder sbSourceLower = new StringBuilder(source.toLowerCase()); + if (source == null || target == null || replacement == null) { + return source; + } + String searchString = target.toLowerCase(); - - int idx = 0; - while((idx = sbSourceLower.indexOf(searchString, idx)) != -1) { - sbSource.replace(idx, idx + searchString.length(), replacement); - sbSourceLower.replace(idx, idx + searchString.length(), replacement); - idx+= replacement.length(); + String sourceLower = source.toLowerCase(); + + int idx = sourceLower.indexOf(searchString); + if (idx == -1) { + return source; } - sbSourceLower.setLength(0); - sbSourceLower.trimToSize(); - sbSourceLower = null; - + + StringBuilder sbSource = new StringBuilder(source.length()); + int lastIdx = 0; + + while (idx != -1) { + sbSource.append(source, lastIdx, idx); + sbSource.append(replacement); + lastIdx = idx + searchString.length(); + idx = sourceLower.indexOf(searchString, lastIdx); + } + sbSource.append(source, lastIdx, source.length()); + return sbSource.toString(); } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/StringSimilarity.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/StringSimilarity.java index 0d6b242..5338820 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/StringSimilarity.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/StringSimilarity.java @@ -3,6 +3,10 @@ public class StringSimilarity { public static double similarity(String s1, String s2) { + if (s1.equals(s2)) { + return 1.0; + } + String longer = s1, shorter = s2; if (s1.length() < s2.length()) { longer = s2; @@ -12,31 +16,45 @@ public static double similarity(String s1, String s2) { if (longerLength == 0) { return 1.0; } + + int lengthDiff = longerLength - shorter.length(); + if (lengthDiff > longerLength / 2) { + return (double)(longerLength - lengthDiff) / longerLength; + } + return (longerLength - editDistance(longer, shorter)) / (double) longerLength; } public static int editDistance(String s1, String s2) { - s1 = s1.toLowerCase(); - s2 = s2.toLowerCase(); - - int[] costs = new int[s2.length() + 1]; - for (int i = 0; i <= s1.length(); i++) { + // Convert to lowercase once + String s1Lower = s1.toLowerCase(); + String s2Lower = s2.toLowerCase(); + + int len1 = s1Lower.length(); + int len2 = s2Lower.length(); + + // Early exit for empty strings + if (len1 == 0) return len2; + if (len2 == 0) return len1; + + int[] costs = new int[len2 + 1]; + for (int i = 0; i <= len1; i++) { int lastValue = i; - for (int j = 0; j <= s2.length(); j++) { - if (i == 0) costs[j] = j; - else { - if (j > 0) { - int newValue = costs[j - 1]; - if (s1.charAt(i - 1) != s2.charAt(j - 1)) - newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1; - costs[j - 1] = lastValue; - lastValue = newValue; + for (int j = 0; j <= len2; j++) { + if (i == 0) { + costs[j] = j; + } else if (j > 0) { + int newValue = costs[j - 1]; + if (s1Lower.charAt(i - 1) != s2Lower.charAt(j - 1)) { + newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1; } + costs[j - 1] = lastValue; + lastValue = newValue; } } - if (i > 0) costs[s2.length()] = lastValue; + if (i > 0) costs[len2] = lastValue; } - return costs[s2.length()]; + return costs[len2]; } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/UnicodeWrapper.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/UnicodeWrapper.java index 9253ad9..8123325 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/UnicodeWrapper.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/UnicodeWrapper.java @@ -4,10 +4,14 @@ public class UnicodeWrapper { private final String start; private final String end; + private final int startInt; + private final int endInt; public UnicodeWrapper(String start, String end) { this.start = start; this.end = end; + this.startInt = Integer.parseInt(start, 16); + this.endInt = Integer.parseInt(end, 16); } public String getStart() { @@ -17,5 +21,13 @@ public String getStart() { public String getEnd() { return this.end; } + + public int getStartInt() { + return this.startInt; + } + + public int getEndInt() { + return this.endInt; + } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/lang/LangManager.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/lang/LangManager.java index 15c2a34..9cb727f 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/lang/LangManager.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/lang/LangManager.java @@ -1,12 +1,16 @@ package a4.papers.chatfilter.chatfilter.shared.lang; -import a4.papers.chatfilter.chatfilter.ChatFilter; - import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.*; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import a4.papers.chatfilter.chatfilter.ChatFilter; public class LangManager { @@ -26,16 +30,18 @@ public String mapToString(String s) { } public String stringArrayToString(String[] strArr) { - StringBuilder sb = new StringBuilder(); - String prefix = ""; - for (String str : strArr) - if (strArr.length > 1) { - sb.append(prefix); - prefix = ", "; - sb.append(str.replace(" ", "")); - } else { - sb.append(str.replace(" ", "")); - } + if (strArr.length == 0) { + return ""; + } + if (strArr.length == 1) { + return strArr[0].replace(" ", ""); + } + + StringBuilder sb = new StringBuilder(strArr.length * 10); + sb.append(strArr[0].replace(" ", "")); + for (int i = 1; i < strArr.length; i++) { + sb.append(", ").append(strArr[i].replace(" ", "")); + } return sb.toString(); } @@ -54,7 +60,7 @@ public void loadLang() throws MalformedURLException { } Map convertResourceBundleToMap(ResourceBundle resource) { - Map map = new HashMap(); + Map map = new HashMap<>(); Enumeration keys = resource.getKeys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); @@ -94,6 +100,7 @@ private void setupLanguageFiles() { if (!lang_plFile.exists()) { chatFilter.saveResource("messages_pl.properties", false); } + break; case "da": locale = DanishLocale; File lang_daFile = new File(chatFilter.getDataFolder().getAbsolutePath(), "messages_da.properties"); diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/LoadFilters.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/LoadFilters.java index bf3f769..7deff73 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/LoadFilters.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/LoadFilters.java @@ -1,12 +1,13 @@ package a4.papers.chatfilter.chatfilter.shared.regexHandler; +import java.util.List; +import java.util.regex.Pattern; + +import org.bukkit.configuration.ConfigurationSection; + import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; import a4.papers.chatfilter.chatfilter.shared.UnicodeWrapper; -import org.bukkit.configuration.ConfigurationSection; - -import java.util.List; -import java.util.regex.Pattern; public class LoadFilters { @@ -155,13 +156,11 @@ public void reloadFilters() { } public void regexCompile() { - for (String StringMatchedDNS : chatFilter.regexAdvert.keySet()) { - Pattern p = Pattern.compile(StringMatchedDNS); - chatFilter.advertRegexPattern.add(p); + for (String pattern : chatFilter.regexAdvert.keySet()) { + chatFilter.advertRegexPattern.add(Pattern.compile(pattern)); } - for (String StringMatchedWords : chatFilter.regexWords.keySet()) { - Pattern p = Pattern.compile(StringMatchedWords); - chatFilter.wordRegexPattern.add(p); + for (String pattern : chatFilter.regexWords.keySet()) { + chatFilter.wordRegexPattern.add(Pattern.compile(pattern)); } } } diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/RegexpGenerator.java b/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/RegexpGenerator.java index 1294814..00a4e23 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/RegexpGenerator.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/regexHandler/RegexpGenerator.java @@ -10,7 +10,7 @@ public RegexpGenerator(ChatFilter instance) { } public String generateRegexp(String s) { - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(s.length() * 15); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); String chars = String.valueOf(c); @@ -43,9 +43,11 @@ public String generateRegexp(String s) { } String toLeetSpeak(String speak) { - StringBuilder sb = new StringBuilder(speak.length()); - if (chatFilter.enableLeetSpeak) - for (char c : speak.toCharArray()) { + if (!chatFilter.enableLeetSpeak) { + return speak; + } + StringBuilder sb = new StringBuilder(speak.length() * 2); + for (char c : speak.toCharArray()) { switch (c) { case 'a': sb.append("@|a|4"); @@ -129,7 +131,7 @@ String toLeetSpeak(String speak) { sb.append(c); break; } - } + } return sb.toString(); } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 39ce42d..0b156ca 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -58,6 +58,9 @@ settings: # Block fancy text? Often used with hacked clients. blockFancyChat: true + # Block non-English letters (allow numbers and symbols) + blockCustomSybols: true + # Prevent same or repeated messages antiRepeatEnabled: true diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9849673..351a25e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: ChatFilter -version: 2.0.14 +version: ${version} main: a4.papers.chatfilter.chatfilter.ChatFilter -api-version: 1.13 +api-version: 1.21 +folia-supported: true authors: [ A4_Papers ] description: Basic Regex chat, book and anvil filter commands: