From f7d8336b2e3deeaee6292245a30af732edbef7d2 Mon Sep 17 00:00:00 2001 From: HSGamer Date: Thu, 11 May 2023 11:18:25 +0700 Subject: [PATCH 1/6] init folia support --- pom.xml | 21 +- .../chatfilter/chatfilter/ChatFilter.java | 20 +- .../papers/chatfilter/chatfilter/Metrics.java | 786 ------------------ .../chatfilter/commands/CommandHandler.java | 11 +- .../chatfilter/events/BooksListener.java | 18 +- src/main/resources/plugin.yml | 1 + 6 files changed, 40 insertions(+), 817 deletions(-) delete mode 100644 src/main/java/a4/papers/chatfilter/chatfilter/Metrics.java diff --git a/pom.xml b/pom.xml index 91f2d86..7912c8c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,16 @@ false - C:\Users\A4pap\Desktop\Output\ChatFilter.jar + + + org.bstats + a4.papers.chatfilter.chatfilter.metrics + + + me.hsgamer.hscore + a4.papers.chatfilter.chatfilter.hscore + + @@ -71,5 +80,15 @@ 1.18.1-R0.1-SNAPSHOT provided + + org.bstats + bstats-bukkit + 3.0.2 + + + me.hsgamer + hscore-bukkit-scheduler + 4.3.4 + diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java b/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java index 5a20647..16002da 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/ChatFilter.java @@ -12,6 +12,9 @@ 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 org.bstats.bukkit.Metrics; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.ConsoleCommandSender; @@ -167,18 +170,9 @@ public void onEnable() { logMsg("[ChatFilter] " + (byPassWords.size() + byPassWords.size()) + " whitelisted items."); int pluginId = 13946; Metrics metrics = new Metrics(this, pluginId); - metrics.addCustomChart(new Metrics.SimplePie("used_language", () -> { - return getLang().locale.toString(); - })); - 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 SimplePie("used_language", () -> getLang().locale.toString())); + metrics.addCustomChart(new SimplePie("total_filters", () -> String.valueOf(regexWords.size() + regexAdvert.size()))); + metrics.addCustomChart(new SingleLineChart("block", () -> blockedInt)); } @Override @@ -288,7 +282,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 = blockedInt + 1; consoleSender.sendMessage("------- Match Type: " + type.id + " ~ " + pl.toUpperCase()); consoleSender.sendMessage("Match: " + regexUsed); consoleSender.sendMessage("Catch > " + p.getName() + ": " + msg); diff --git a/src/main/java/a4/papers/chatfilter/chatfilter/Metrics.java b/src/main/java/a4/papers/chatfilter/chatfilter/Metrics.java deleted file mode 100644 index b9fbe4a..0000000 --- a/src/main/java/a4/papers/chatfilter/chatfilter/Metrics.java +++ /dev/null @@ -1,786 +0,0 @@ -package a4.papers.chatfilter.chatfilter; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; - -import javax.net.ssl.HttpsURLConnection; -import java.io.*; -import java.lang.reflect.Method; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.zip.GZIPOutputStream; - -public class Metrics { - - private final Plugin plugin; - - private final MetricsBase metricsBase; - - /** - * Creates a new Metrics instance. - * - * @param plugin Your plugin instance. - * @param serviceId The id of the service. It can be found at What is my plugin id? - */ - public Metrics(JavaPlugin plugin, int serviceId) { - this.plugin = plugin; - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - if (!config.isSet("serverUuid")) { - config.addDefault("enabled", true); - config.addDefault("serverUuid", UUID.randomUUID().toString()); - config.addDefault("logFailedRequests", false); - config.addDefault("logSentData", false); - config.addDefault("logResponseStatusText", false); - // Inform the server owners about bStats - config.options().header("bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + "anonymous.").copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ignored) { - } - } - // Load the data - boolean enabled = config.getBoolean("enabled", true); - String serverUUID = config.getString("serverUuid"); - boolean logErrors = config.getBoolean("logFailedRequests", false); - boolean logSentData = config.getBoolean("logSentData", false); - boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); - metricsBase = new MetricsBase("bukkit", serverUUID, serviceId, enabled, this::appendPlatformData, this::appendServiceData, submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), plugin::isEnabled, (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), (message) -> this.plugin.getLogger().log(Level.INFO, message), logErrors, logSentData, logResponseStatusText); - } - - /** - * Adds a custom chart. - * - * @param chart The chart to add. - */ - public void addCustomChart(CustomChart chart) { - metricsBase.addCustomChart(chart); - } - - private void appendPlatformData(JsonObjectBuilder builder) { - builder.appendField("playerAmount", getPlayerAmount()); - builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); - builder.appendField("bukkitVersion", Bukkit.getVersion()); - builder.appendField("bukkitName", Bukkit.getName()); - builder.appendField("javaVersion", System.getProperty("java.version")); - builder.appendField("osName", System.getProperty("os.name")); - builder.appendField("osArch", System.getProperty("os.arch")); - builder.appendField("osVersion", System.getProperty("os.version")); - builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); - } - - private void appendServiceData(JsonObjectBuilder builder) { - builder.appendField("pluginVersion", plugin.getDescription().getVersion()); - } - - private int getPlayerAmount() { - try { - // Around MC 1.8 the return type was changed from an array to a collection, - // This fixes java.lang.NoSuchMethodError: - // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - return onlinePlayersMethod.getReturnType().equals(Collection.class) ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - // Just use the new method if the reflection failed - return Bukkit.getOnlinePlayers().size(); - } - } - - public static class MetricsBase { - - /** - * The version of the Metrics class. - */ - public static final String METRICS_VERSION = "2.2.1"; - - private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics")); - - private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; - - private final String platform; - - private final String serverUuid; - - private final int serviceId; - - private final Consumer appendPlatformDataConsumer; - - private final Consumer appendServiceDataConsumer; - - private final Consumer submitTaskConsumer; - - private final Supplier checkServiceEnabledSupplier; - - private final BiConsumer errorLogger; - - private final Consumer infoLogger; - - private final boolean logErrors; - - private final boolean logSentData; - - private final boolean logResponseStatusText; - - private final Set customCharts = new HashSet<>(); - - private final boolean enabled; - - /** - * Creates a new MetricsBase class instance. - * - * @param platform The platform of the service. - * @param serviceId The id of the service. - * @param serverUuid The server uuid. - * @param enabled Whether or not data sending is enabled. - * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and - * appends all platform-specific data. - * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and - * appends all service-specific data. - * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be - * used to delegate the data collection to a another thread to prevent errors caused by - * concurrency. Can be {@code null}. - * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. - * @param errorLogger A consumer that accepts log message and an error. - * @param infoLogger A consumer that accepts info log messages. - * @param logErrors Whether or not errors should be logged. - * @param logSentData Whether or not the sent data should be logged. - * @param logResponseStatusText Whether or not the response status text should be logged. - */ - public MetricsBase(String platform, String serverUuid, int serviceId, boolean enabled, Consumer appendPlatformDataConsumer, Consumer appendServiceDataConsumer, Consumer submitTaskConsumer, Supplier checkServiceEnabledSupplier, BiConsumer errorLogger, Consumer infoLogger, boolean logErrors, boolean logSentData, boolean logResponseStatusText) { - this.platform = platform; - this.serverUuid = serverUuid; - this.serviceId = serviceId; - this.enabled = enabled; - this.appendPlatformDataConsumer = appendPlatformDataConsumer; - this.appendServiceDataConsumer = appendServiceDataConsumer; - this.submitTaskConsumer = submitTaskConsumer; - this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; - this.errorLogger = errorLogger; - this.infoLogger = infoLogger; - this.logErrors = logErrors; - this.logSentData = logSentData; - this.logResponseStatusText = logResponseStatusText; - checkRelocation(); - if (enabled) { - startSubmitting(); - } - } - - /** - * Gzips the given string. - * - * @param str The string to gzip. - * @return The gzipped string. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - } - return outputStream.toByteArray(); - } - - public void addCustomChart(CustomChart chart) { - this.customCharts.add(chart); - } - - private void startSubmitting() { - final Runnable submitTask = () -> { - if (!enabled || !checkServiceEnabledSupplier.get()) { - // Submitting data or service is disabled - scheduler.shutdown(); - return; - } - if (submitTaskConsumer != null) { - submitTaskConsumer.accept(this::submitData); - } else { - this.submitData(); - } - }; - // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution - // of requests on the - // bStats backend. To circumvent this problem, we introduce some randomness into the initial - // and second delay. - // WARNING: You must not modify and part of this Metrics class, including the submit delay or - // frequency! - // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! - long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); - long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); - scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); - scheduler.scheduleAtFixedRate(submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); - } - - private void submitData() { - final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); - appendPlatformDataConsumer.accept(baseJsonBuilder); - final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); - appendServiceDataConsumer.accept(serviceJsonBuilder); - JsonObjectBuilder.JsonObject[] chartData = customCharts.stream().map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)).filter(Objects::nonNull).toArray(JsonObjectBuilder.JsonObject[]::new); - serviceJsonBuilder.appendField("id", serviceId); - serviceJsonBuilder.appendField("customCharts", chartData); - baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); - baseJsonBuilder.appendField("serverUUID", serverUuid); - baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); - JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); - scheduler.execute(() -> { - try { - // Send the data - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logErrors) { - errorLogger.accept("Could not submit bStats metrics data", e); - } - } - }); - } - - private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { - if (logSentData) { - infoLogger.accept("Sent bStats metrics data: " + data.toString()); - } - String url = String.format(REPORT_URL, platform); - HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("User-Agent", "Metrics-Service/1"); - connection.setDoOutput(true); - try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { - outputStream.write(compressedData); - } - StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - builder.append(line); - } - } - if (logResponseStatusText) { - infoLogger.accept("Sent data to bStats and received response: " + builder); - } - } - - /** - * Checks that the class was properly relocated. - */ - private void checkRelocation() { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little - // "trick" ... :D - final String defaultPackage = new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); - final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); - // We want to make sure no one just copy & pastes the example and uses the wrong package - // names - if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } - } - } - - public static class AdvancedBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } - } - - public static class SimpleBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimpleBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - for (Map.Entry entry : map.entrySet()) { - valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()}); - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } - } - - public static class MultiLineChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public MultiLineChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } - } - - public static class AdvancedPie extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - // Skip this invalid - continue; - } - allSkipped = false; - valuesBuilder.appendField(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } - } - - public abstract static class CustomChart { - - private final String chartId; - - protected CustomChart(String chartId) { - if (chartId == null) { - throw new IllegalArgumentException("chartId must not be null"); - } - this.chartId = chartId; - } - - public JsonObjectBuilder.JsonObject getRequestJsonObject(BiConsumer errorLogger, boolean logErrors) { - JsonObjectBuilder builder = new JsonObjectBuilder(); - builder.appendField("chartId", chartId); - try { - JsonObjectBuilder.JsonObject data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - builder.appendField("data", data); - } catch (Throwable t) { - if (logErrors) { - errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return builder.build(); - } - - protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; - } - - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObjectBuilder.JsonObject getChartData() throws Exception { - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("value", value).build(); - } - } - - public static class DrilldownPie extends CustomChart { - - private final Callable>> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } - - @Override - public JsonObjectBuilder.JsonObject getChartData() throws Exception { - JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); - } - } - - /** - * An extremely simple JSON builder. - * - *

While this class is neither feature-rich nor the most performant one, it's sufficient enough - * for its use-case. - */ - public static class JsonObjectBuilder { - - private StringBuilder builder = new StringBuilder(); - - private boolean hasAtLeastOneField = false; - - public JsonObjectBuilder() { - builder.append("{"); - } - - /** - * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. - * - *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. - * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). - * - * @param value The value to escape. - * @return The escaped value. - */ - private static String escape(String value) { - final StringBuilder builder = new StringBuilder(); - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '"') { - builder.append("\\\""); - } else if (c == '\\') { - builder.append("\\\\"); - } else if (c <= '\u000F') { - builder.append("\\u000").append(Integer.toHexString(c)); - } else if (c <= '\u001F') { - builder.append("\\u00").append(Integer.toHexString(c)); - } else { - builder.append(c); - } - } - return builder.toString(); - } - - /** - * Appends a null field to the JSON. - * - * @param key The key of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendNull(String key) { - appendFieldUnescaped(key, "null"); - return this; - } - - /** - * Appends a string field to the JSON. - * - * @param key The key of the field. - * @param value The value of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, String value) { - if (value == null) { - throw new IllegalArgumentException("JSON value must not be null"); - } - appendFieldUnescaped(key, "\"" + escape(value) + "\""); - return this; - } - - /** - * Appends an integer field to the JSON. - * - * @param key The key of the field. - * @param value The value of the field. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, int value) { - appendFieldUnescaped(key, String.valueOf(value)); - return this; - } - - /** - * Appends an object to the JSON. - * - * @param key The key of the field. - * @param object The object. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, JsonObject object) { - if (object == null) { - throw new IllegalArgumentException("JSON object must not be null"); - } - appendFieldUnescaped(key, object.toString()); - return this; - } - - /** - * Appends a string array to the JSON. - * - * @param key The key of the field. - * @param values The string array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, String[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = Arrays.stream(values).map(value -> "\"" + escape(value) + "\"").collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } - - /** - * Appends an integer array to the JSON. - * - * @param key The key of the field. - * @param values The integer array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, int[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } - - /** - * Appends an object array to the JSON. - * - * @param key The key of the field. - * @param values The integer array. - * @return A reference to this object. - */ - public JsonObjectBuilder appendField(String key, JsonObject[] values) { - if (values == null) { - throw new IllegalArgumentException("JSON values must not be null"); - } - String escapedValues = Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); - appendFieldUnescaped(key, "[" + escapedValues + "]"); - return this; - } - - /** - * Appends a field to the object. - * - * @param key The key of the field. - * @param escapedValue The escaped value of the field. - */ - private void appendFieldUnescaped(String key, String escapedValue) { - if (builder == null) { - throw new IllegalStateException("JSON has already been built"); - } - if (key == null) { - throw new IllegalArgumentException("JSON key must not be null"); - } - if (hasAtLeastOneField) { - builder.append(","); - } - builder.append("\"").append(escape(key)).append("\":").append(escapedValue); - hasAtLeastOneField = true; - } - - /** - * Builds the JSON string and invalidates this builder. - * - * @return The built JSON string. - */ - public JsonObject build() { - if (builder == null) { - throw new IllegalStateException("JSON has already been built"); - } - JsonObject object = new JsonObject(builder.append("}").toString()); - builder = null; - return object; - } - - /** - * A super simple representation of a JSON object. - * - *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not - * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, - * JsonObject)}. - */ - public static class JsonObject { - - private final String value; - - private JsonObject(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - } - } -} 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..39a0d7e 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/commands/CommandHandler.java @@ -2,9 +2,9 @@ import a4.papers.chatfilter.chatfilter.ChatFilter; import a4.papers.chatfilter.chatfilter.shared.FilterWrapper; +import me.hsgamer.hscore.bukkit.scheduler.Scheduler; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; public class CommandHandler { ChatFilter chatFilter; @@ -22,12 +22,9 @@ 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); + Scheduler.plugin(chatFilter).sync().runTask(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), s.replace("", "").replace("%player%", p.getName()).replace("%item%", firstWord)); + }); } } } 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..33ec463 100644 --- a/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java +++ b/src/main/java/a4/papers/chatfilter/chatfilter/events/BooksListener.java @@ -5,7 +5,7 @@ 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 me.hsgamer.hscore.bukkit.scheduler.Scheduler; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -14,7 +14,6 @@ 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; @@ -83,14 +82,13 @@ public void onBookEvent(PlayerEditBookEvent event) { } 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); + Player player = event.getPlayer(); + player.getInventory().getItemInMainHand().setAmount(event.getPlayer().getInventory().getItemInMainHand().getAmount() - 1); + Scheduler.plugin(chatFilter).sync().runEntityTaskLater( + player, + () -> player.getInventory().addItem(new ItemStack(Material.WRITABLE_BOOK, 1)), + 1 + ); } if (filterWrapper.getLogToConsole()) chatFilter.sendConsole(type, bookPagesList.get(nom - 1), p, filterWrapper.getRegex(), "Book"); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9849673..c8f1c8e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,6 +2,7 @@ name: ChatFilter version: 2.0.14 main: a4.papers.chatfilter.chatfilter.ChatFilter api-version: 1.13 +folia-supported: true authors: [ A4_Papers ] description: Basic Regex chat, book and anvil filter commands: From aa132ad8d2799adb478bf9021d7cd7ecc25c01b3 Mon Sep 17 00:00:00 2001 From: HSGamer Date: Thu, 11 May 2023 11:22:25 +0700 Subject: [PATCH 2/6] minimize the jar --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7912c8c..8c784b8 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ false + true org.bstats From f9ced661099ccba985d224480c5786a4b5507cc8 Mon Sep 17 00:00:00 2001 From: zepsizola Date: Wed, 2 Apr 2025 15:51:17 +0100 Subject: [PATCH 3/6] Changed version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8c784b8..21a2d18 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ a4.papers.chatfilter chatFilter - 1.1.8 + 1.1.9 jar ChatFilter From 70104f2c664ca09c1a03977ae8c92465ba90d150 Mon Sep 17 00:00:00 2001 From: zepsizola Date: Wed, 2 Apr 2025 15:55:29 +0100 Subject: [PATCH 4/6] Changed version number in plugin.yml --- src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c8f1c8e..f136500 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ChatFilter -version: 2.0.14 +version: 2.0.15 main: a4.papers.chatfilter.chatfilter.ChatFilter api-version: 1.13 folia-supported: true From 6f550465fa21e51645aa7d85d861c4bc1bc63f6d Mon Sep 17 00:00:00 2001 From: ZepsiZola <49735083+ZepsiZola@users.noreply.github.com> Date: Mon, 14 Apr 2025 01:38:50 +0100 Subject: [PATCH 5/6] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index db7c83c..dc3c0b1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # 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. + +## Folia-compatible fork From 805cb37513eff5bc4ed57d475757141a82740bd7 Mon Sep 17 00:00:00 2001 From: zepsizola Date: Mon, 14 Apr 2025 15:11:33 +0100 Subject: [PATCH 6/6] README.md --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dc3c0b1..d315a19 100644 --- a/README.md +++ b/README.md @@ -1,4 +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! -## Folia-compatible fork +## 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 is a fork of an older plugin by the same name. This fork implements Folia.