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..c806b2a --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + java + id("com.gradleup.shadow") version "9.3.1" + id("io.papermc.paperweight.userdev") version "2.0.0-beta.19" +} + +group = "a4.papers.chatfilter" +version = "2.0.15" + +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..f152ae6 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java @@ -1,19 +1,15 @@ 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 org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -28,18 +24,38 @@ 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.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(); + public ConsoleCommandSender consoleSender = Bukkit.getConsoleSender(); public ChatFilter chatFilter; public ChatFilters chatFilters; @@ -69,6 +85,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; @@ -105,7 +122,8 @@ public class ChatFilter extends JavaPlugin { public String perWordOptionsString; 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 +134,7 @@ public class ChatFilter extends JavaPlugin { private FileConfiguration unicodeConfig; + @Override public void onEnable() { regexWords = new HashMap<>(); regexAdvert = new HashMap<>(); @@ -132,6 +151,10 @@ public void onEnable() { } catch (MalformedURLException e) { e.printStackTrace(); } + + scheduler = UniversalScheduler.getScheduler(this); + instance = this; + createCustomConfig(); loadVariables(); getCommand("chatfilter").setExecutor(new CommandMain(this)); @@ -173,12 +196,7 @@ 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 @@ -199,6 +217,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"); @@ -230,15 +249,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 +301,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 +310,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 +371,35 @@ 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; + } } 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/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/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..276ea7a 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,9 @@ 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.List; + import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -16,9 +14,11 @@ 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,7 +33,7 @@ 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<>(); List bookPageMatch = new ArrayList<>(); @@ -99,9 +99,10 @@ 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); } 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..6d48a07 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.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.plugin.EventExecutor; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import a4.papers.chatfilter.chatfilter.ChatFilter; public class CapsChatListener implements EventExecutor, Listener { @@ -26,7 +26,8 @@ public void execute(final Listener listener, final Event event) throws EventExce public void onPlayerCaps(AsyncPlayerChatEvent event) { String msg = event.getMessage(); 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,11 +40,11 @@ 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); @@ -51,12 +52,6 @@ public void onPlayerCaps(AsyncPlayerChatEvent event) { } 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..f92892b 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; @@ -21,12 +20,19 @@ public class ChatDelayListener implements EventExecutor, Listener { public Map chatmsgs = new HashMap(); ChatFilter chatFilter; - private HashMap chatMSG = new HashMap<>(); - private HashMap cooldown = new HashMap<>(); + private BigDecimal similarityThreshold; public ChatDelayListener(ChatFilter instance) { chatFilter = instance; } + + private BigDecimal getSimilarityThreshold() { + if (similarityThreshold == null) { + String percent = chatFilter.percentage.trim().replace("%", ""); + similarityThreshold = new BigDecimal(percent).divide(BigDecimal.valueOf(100)); + } + return similarityThreshold; + } @Override public void execute(final Listener listener, final Event event) throws EventException { @@ -38,38 +44,38 @@ public void onPlayerSpam(AsyncPlayerChatEvent 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); + 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); - } + + 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().doubleValue()) { + 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..5bf87a2 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.event.player.AsyncPlayerChatEvent; +import org.bukkit.plugin.EventExecutor; 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; @@ -20,18 +25,17 @@ public void execute(final Listener listener, final Event event) throws EventExce @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerChatPause(AsyncPlayerChatEvent event) { - if (event.isCancelled()) + 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() + ": " + event.getMessage()); } } 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..a3dff2a 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 java.util.regex.Matcher; -import java.util.regex.Pattern; +import a4.papers.chatfilter.chatfilter.ChatFilter; public class RepeatCharListener implements EventExecutor, Listener { @@ -26,7 +24,8 @@ public void execute(final Listener listener, final Event event) throws EventExce public void onPlayerCarSpam(AsyncPlayerChatEvent event) { String msg = event.getMessage(); 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)) { @@ -37,12 +36,6 @@ public void onPlayerCarSpam(AsyncPlayerChatEvent event) { } 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..3cc0848 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/SwearChatListener.java @@ -1,13 +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.LowerCaseReplace; -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; @@ -16,6 +8,15 @@ import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.plugin.EventExecutor; +import a4.papers.chatfilter.chatfilter.ChatFilter; +import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import a4.papers.chatfilter.chatfilter.shared.LowerCaseReplace; +import a4.papers.chatfilter.chatfilter.shared.Result; +import a4.papers.chatfilter.chatfilter.shared.Types; +import a4.papers.chatfilter.chatfilter.shared.lang.EnumStrings; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; + public class SwearChatListener implements EventExecutor, Listener { ChatFilter chatFilter; @@ -35,12 +36,27 @@ public void onPlayerSwear(AsyncPlayerChatEvent event) { String chatMessage = ChatColor.stripColor(event.getMessage()).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 rawMessage = ChatColor.stripColor(event.getMessage()); + if (chatFilter.getChatFilters().containsNonEnglishLetters(rawMessage)) { + 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,8 +92,9 @@ 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)); } 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..dc01a2f 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/shared/ChatFilters.java @@ -1,13 +1,17 @@ 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.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.entity.Player; + +import a4.papers.chatfilter.chatfilter.ChatFilter; + public class ChatFilters { ChatFilter chatFilter; @@ -17,118 +21,167 @@ 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"))) { + 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"); + + List groupWords = new ArrayList<>(); + 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(); + regexUsed.add(regex); + if (!groupWords.contains(match)) { + 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(); + if (!groupWords.contains(match)) { + 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; - } + // Build regex map only when needed + Map regexMap = new HashMap<>(); + + // Check URL + if (!canBypassURL && !chatFilter.settingsAllowURL) { + Matcher m = chatFilter.urlPattern.matcher(lowercaseString); + if (m.find()) { + matched = true; + matchedURL = true; + regex = chatFilter.URL_REGEX; 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; } + + // 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); + String[] array = groupWords.toArray(new String[groupWords.size()]); regexMap.putAll(chatFilter.regexWords); regexMap.putAll(chatFilter.regexAdvert); return new Result(matched, array, type, regexMap.get(regex), 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: