diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 93c1363..017c4f1 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -1,28 +1,30 @@ -name: build-and-test -on: - pull_request: - branches: - - master +### COMMENTED OUT FOR NOW SINCE THIS BUILD NO LONGER USES GRADLE ### -jobs: - build-and-test: - runs-on: ubuntu-latest +# name: build-and-test +# on: +# pull_request: +# branches: +# - master - steps: - - name: Checkout - uses: actions/checkout@v4 +# jobs: +# build-and-test: +# runs-on: ubuntu-latest - - name: Set up JDK 1.8 - uses: actions/setup-java@v2 - with: - java-version: 8 - distribution: 'temurin' +# steps: +# - name: Checkout +# uses: actions/checkout@v4 - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v3 +# - name: Set up JDK 1.8 +# uses: actions/setup-java@v2 +# with: +# java-version: 8 +# distribution: 'temurin' - - name: Build with Gradle - run: ./gradlew build +# - name: Set up Gradle +# uses: gradle/actions/setup-gradle@v3 - - name: Test with Gradle - run: ./gradlew test +# - name: Build with Gradle +# run: ./gradlew build + +# - name: Test with Gradle +# run: ./gradlew test diff --git a/pom.xml b/pom.xml index a80ebcb..6f76818 100644 --- a/pom.xml +++ b/pom.xml @@ -17,11 +17,6 @@ - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - johnymuffin-nexus-releases https://repository.johnymuffin.com/repository/maven-public/ diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/ChatGuard.java b/src/main/java/io/github/aleksandarharalanov/chatguard/ChatGuard.java index b2c4e96..162497e 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/ChatGuard.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/ChatGuard.java @@ -1,6 +1,7 @@ package io.github.aleksandarharalanov.chatguard; import io.github.aleksandarharalanov.chatguard.command.ChatGuardCommand; +import io.github.aleksandarharalanov.chatguard.core.config.FilterConfig; import io.github.aleksandarharalanov.chatguard.listener.block.SignChangeListener; import io.github.aleksandarharalanov.chatguard.listener.player.*; import io.github.aleksandarharalanov.chatguard.util.config.ConfigUtil; @@ -64,13 +65,13 @@ public void onEnable() { final ChatGuardCommand command = new ChatGuardCommand(this); getCommand("chatguard").setExecutor(command); - LogUtil.logConsoleInfo(String.format("[%s] v%s Enabled.", + System.out.println(String.format("[%s] v%s Enabled.", getDescription().getName(), getDescription().getVersion())); } @Override public void onDisable() { - LogUtil.logConsoleInfo(String.format("[%s] v%s Disabled.", + System.out.println(String.format("[%s] v%s Disabled.", getDescription().getName(), getDescription().getVersion())); } @@ -93,4 +94,10 @@ public static ConfigUtil getStrikes() { public static ConfigUtil getCaptchas() { return captchas; } + + public static void reloadConfig() { + getConfig().loadAndLog(); + + FilterConfig.generateBlackListCache(); + } } \ No newline at end of file diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/CaptchaCommand.java b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/CaptchaCommand.java index b537ba7..b781e6b 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/CaptchaCommand.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/CaptchaCommand.java @@ -6,7 +6,6 @@ import io.github.aleksandarharalanov.chatguard.util.auth.AccessUtil; import io.github.aleksandarharalanov.chatguard.core.misc.AudioCuePlayer; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -49,7 +48,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St CaptchaConfig.removePlayerCaptcha(player.getName()); AudioCuePlayer.play(LogType.CAPTCHA, player, true); - LogUtil.logConsoleInfo(String.format("[ChatGuard] Player '%s' passed captcha verification.", player.getName())); + System.out.println(String.format("[ChatGuard] Player '%s' passed captcha verification.", player.getName())); return true; } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/HelpCommand.java b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/HelpCommand.java index ee2c5c4..d4837c7 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/HelpCommand.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/HelpCommand.java @@ -1,6 +1,5 @@ package io.github.aleksandarharalanov.chatguard.command.subcommand; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -23,7 +22,7 @@ public static void sendHelp(CommandSender sender) { if (sender instanceof Player) { sender.sendMessage(ColorUtil.translateColorCodes(message)); } else { - LogUtil.logConsoleInfo(message.replaceAll("&.", "")); + System.out.println(message.replaceAll("&.", "")); } } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/ReloadCommand.java b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/ReloadCommand.java index d412403..0c523f6 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/ReloadCommand.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/ReloadCommand.java @@ -2,7 +2,6 @@ import io.github.aleksandarharalanov.chatguard.ChatGuard; import io.github.aleksandarharalanov.chatguard.util.auth.AccessUtil; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -20,9 +19,9 @@ public boolean onCommand(CommandSender sender, Command command, String label, St if (sender instanceof Player) { sender.sendMessage(ColorUtil.translateColorCodes("&a[ChatGuard] Configurations reloaded.")); } - LogUtil.logConsoleInfo("[ChatGuard] Configurations reloaded."); + System.out.println("[ChatGuard] Configurations reloaded."); - ChatGuard.getConfig().loadAndLog(); + ChatGuard.reloadConfig(); ChatGuard.getDiscord().loadAndLog(); ChatGuard.getStrikes().loadAndLog(); ChatGuard.getCaptchas().loadAndLog(); diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/StrikeCommand.java b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/StrikeCommand.java index 023c040..8d7dc97 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/StrikeCommand.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/StrikeCommand.java @@ -3,7 +3,6 @@ import io.github.aleksandarharalanov.chatguard.core.config.PenaltyConfig; import io.github.aleksandarharalanov.chatguard.util.auth.AccessUtil; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -20,7 +19,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } if (args.length < 2) { - sender.sendMessage(ColorUtil.translateColorCodes("&cUsage: /cg strike [0-5]")); + sender.sendMessage(ColorUtil.translateColorCodes("&cUsage: /cg strike [strikes]")); return true; } @@ -50,8 +49,8 @@ public boolean onCommand(CommandSender sender, Command command, String label, St try { int newStrike = Integer.parseInt(args[2]); - if (newStrike < 0 || newStrike > 5) { - sender.sendMessage(ColorUtil.translateColorCodes("&c[ChatGuard] Invalid range. Choose from &e0 &cto &e5&c.")); + if (newStrike < 0) { + sender.sendMessage(ColorUtil.translateColorCodes("&c[ChatGuard] Must be greater than &e0.")); return true; } @@ -64,12 +63,12 @@ public boolean onCommand(CommandSender sender, Command command, String label, St ))); } - LogUtil.logConsoleInfo(String.format( + System.out.println(String.format( "[ChatGuard] Player '%s' set from strike %d to %d.", foundKey, playerStrikeTier, newStrike )); } catch (NumberFormatException e) { - sender.sendMessage(ColorUtil.translateColorCodes("&c[ChatGuard] Invalid input. Enter a number from &e0 &cto &e5&c.")); + sender.sendMessage(ColorUtil.translateColorCodes("&c[ChatGuard] Invalid input. Enter a number.")); } return true; diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterConfig.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterConfig.java index 10298fe..96855d2 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterConfig.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterConfig.java @@ -1,6 +1,7 @@ package io.github.aleksandarharalanov.chatguard.core.config; import io.github.aleksandarharalanov.chatguard.ChatGuard; +import io.github.aleksandarharalanov.chatguard.core.security.common.TimeFormatter; import java.util.ArrayList; import java.util.Arrays; @@ -8,6 +9,8 @@ public final class FilterConfig { + private static List blacklist; + private FilterConfig() {} public static boolean getChatEnabled() { @@ -38,24 +41,79 @@ public static boolean getAutoMuteEnabled() { return ChatGuard.getConfig().getBoolean("filter.auto-mute.enabled", true); } + public static boolean getStrikeDecayEnabled() { + return ChatGuard.getConfig().getBoolean("filter.auto-mute.strike-decay.enabled", true); + } + + public static long getStrikeDecayPeriod() { + final String configString = ChatGuard.getConfig().getString("filter.auto-mute.strike-decay.period"); + + try { + final long futureTime = TimeFormatter.parseDateDiff(configString, true); + + return futureTime - System.currentTimeMillis(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + + public static int getWarningcount() { + return ChatGuard.getConfig().getInt("filter.auto-mute.warnings.warning-count", 0); + } + + public static int getWarningBypassThreashold() { + return ChatGuard.getConfig().getInt("filter.auto-mute.warnings.severity-bypass-threashold", 3); + } + public static List getAutoMuteDurations() { List def = Arrays.asList("30m", "1h", "2h", "4h", "8h", "24h"); return ChatGuard.getConfig().getStringList("filter.auto-mute.duration", def); } - public static List getTermsWhitelist() { - return ChatGuard.getConfig().getStringList("filter.rules.whitelist.terms", new ArrayList<>()); + public static List getWhitelist() { + return ChatGuard.getConfig().getStringList("filter.rules.whitelist", new ArrayList<>()); } - public static List getRegexWhitelist() { - return ChatGuard.getConfig().getStringList("filter.rules.whitelist.regex", new ArrayList<>()); - } + public static List getBlacklist() { + if (blacklist == null) + generateBlackListCache(); - public static List getTermsBlacklist() { - return ChatGuard.getConfig().getStringList("filter.rules.blacklist.terms", new ArrayList<>()); + return blacklist; } - public static List getRegexBlacklist() { - return ChatGuard.getConfig().getStringList("filter.rules.blacklist.regex", new ArrayList<>()); + public static void generateBlackListCache() { + List entries = ChatGuard.getConfig().getList("filter.rules.blacklist"); + + List newBlackList = new ArrayList<>(); + + try { + for (Object entry : entries) { + final FilterTerm filterTerm; + + List pair = (List) entry; + if (pair.size() == 2) { + final String name = (String) pair.get(0); + final String filter = (String) pair.get(1); + + filterTerm = new FilterTerm(name, filter); + } else { + final String name = (String) pair.get(0); + final String filter = (String) pair.get(1); + + final int severity = (int) pair.get(2); + + filterTerm = new FilterTerm(name, filter, severity); + } + + newBlackList.add(filterTerm); + } + + blacklist = newBlackList; + } + catch( + ClassCastException e + ) { + throw new RuntimeException("unknown type in config for blacklist. use either (String, String, Int) or (String, String). if confused, ask RitzKid76"); + } } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterTerm.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterTerm.java new file mode 100644 index 0000000..8ab3500 --- /dev/null +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/FilterTerm.java @@ -0,0 +1,56 @@ +package io.github.aleksandarharalanov.chatguard.core.config; + +public class FilterTerm { + private String name; + private String filter; + + private int severity = 1; + + public FilterTerm(String name, String filter) { + this.name = name; + this.filter = filter; + } + + public FilterTerm(String name, String filter, int severity) { + this(name, filter); + + this.severity = severity; + } + + public String getName() { + return name; + } + + public String getFilter() { + return filter; + } + + public int getSeverity() { + return severity; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + + if(o == null) + return false; + + if(o instanceof String) + return equalsString((String) o); + if(o instanceof FilterTerm) + return equalsFilterTerm((FilterTerm) o); + + return false; + } + + private boolean equalsString(String s) { + final FilterTerm that = new FilterTerm("", s); + return equalsFilterTerm(that); + } + + private boolean equalsFilterTerm(FilterTerm that) { + return filter == that.getFilter(); + } +} diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/PenaltyConfig.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/PenaltyConfig.java index 7df925d..5dd6079 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/PenaltyConfig.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/config/PenaltyConfig.java @@ -1,6 +1,8 @@ package io.github.aleksandarharalanov.chatguard.core.config; import io.github.aleksandarharalanov.chatguard.ChatGuard; +import io.github.aleksandarharalanov.chatguard.util.config.ConfigUtil; + import org.bukkit.entity.Player; import java.util.List; @@ -13,43 +15,90 @@ public static List getStrikesKeys() { return ChatGuard.getStrikes().getKeys(); } + public static int getPlayerStrike(Player player) { + return getPlayerStrike(player.getName()); + } + public static int getPlayerStrike(String playerName) { - return ChatGuard.getStrikes().getInt(playerName, 0); + return ChatGuard.getStrikes().getInt(playerName + ".strikes", 0); } - public static int getPlayerStrike(Player player) { - return ChatGuard.getStrikes().getInt(player.getName(), 0); + public static int getPlayerWarnings(Player player) { + return getPlayerWarnings(player.getName()); + } + + public static int getPlayerWarnings(String playerName) { + return ChatGuard.getStrikes().getInt(playerName + ".warnings", 0); } - public static boolean isPlayerOnFinalStrike(Player player) { - return ChatGuard.getStrikes().getInt(player.getName(), 0) == 5; + public static void setPlayerStrike(Player player, int newStrike) { + setPlayerStrike(player.getName(), newStrike); + } + + public static void decrementPlayerStrike(Player player, int amount, long updateTime) { + setPlayerStrike(player.getName(), getPlayerStrike(player) - amount, updateTime); + } + + public static void incrementPlayerStrike(Player player, int amount) { + setPlayerStrike(player, getPlayerStrike(player) + amount); } public static void setPlayerStrike(String playerName, int newStrike) { - ChatGuard.getStrikes().setProperty(playerName, newStrike); - ChatGuard.getStrikes().save(); + setPlayerStrike(playerName, newStrike, System.currentTimeMillis()); } - public static void setPlayerStrike(Player player, int newStrike) { - ChatGuard.getStrikes().setProperty(player.getName(), newStrike); + public static void setPlayerStrike(String playerName, int newStrike, long updateTime) { + ConfigUtil strikes = ChatGuard.getStrikes(); + + newStrike = Math.max(newStrike, 0); + + if(newStrike == 0 && getPlayerWarnings(playerName) <= 0) + strikes.removeProperty(playerName); + else { + strikes.setProperty(playerName + ".strikes", newStrike); + strikes.setProperty(playerName + ".updated", updateTime); + } + ChatGuard.getStrikes().save(); } - public static void incrementPlayerStrike(Player player) { - if (ChatGuard.getStrikes().getInt(player.getName(), 0) <= 4) { - ChatGuard.getStrikes().setProperty(player.getName(), ChatGuard.getStrikes().getInt(player.getName(), 0) + 1); - ChatGuard.getStrikes().save(); - } + public static void incrementPlayerWarnings(Player player) { + setPlayerWarnings(player, getPlayerWarnings(player) + 1); } - public static void setDefaultStrikeTier(Player player) { - if (ChatGuard.getStrikes().getInt(player.getName(), -1) == -1) { - ChatGuard.getStrikes().setProperty(player.getName(), 0); - ChatGuard.getStrikes().save(); - } + public static void setPlayerWarnings(Player player, int warnings) { + setPlayerWarnings(player.getName(), warnings); + } + + public static void setPlayerWarnings(String playerName, int warnings) { + ChatGuard.getStrikes().setProperty(playerName + ".warnings", warnings); } public static String getAutoMuteDuration(Player player) { - return FilterConfig.getAutoMuteDurations().get(ChatGuard.getStrikes().getInt(player.getName(), 0)); + final List penalties = FilterConfig.getAutoMuteDurations(); + final int maxPenalty = penalties.size() - 1; + + int playerPenalty = getPlayerStrike(player); + + if (playerPenalty > maxPenalty) + return penalties.get(maxPenalty); + + return penalties.get(playerPenalty); + } + + public static long getLastMuteTime(Player player) { + return getLastMuteTime(player.getName()); + } + + public static long getLastMuteTime(String playerName) { + final String lastUpdatedString = ChatGuard.getStrikes().getString(playerName + ".updated"); + if(lastUpdatedString == null) + return -1; + + try { + return Long.parseLong(lastUpdatedString); + } catch (NumberFormatException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/ChatEmbed.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/ChatEmbed.java index b83cf22..7cfb444 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/ChatEmbed.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/ChatEmbed.java @@ -11,27 +11,30 @@ public final class ChatEmbed extends DiscordEmbed { private final String trigger; + private final int severity; + private final boolean warned; - public ChatEmbed(JavaPlugin plugin, Player player, String content, String trigger) { + public ChatEmbed(JavaPlugin plugin, Player player, String content, String trigger, int severity, boolean warned) { super(plugin, player, content); this.trigger = trigger; + this.severity = severity; + this.warned = warned; setupBaseEmbed(); } @Override protected void setupEmbedDetails() { - if (PenaltyConfig.isPlayerOnFinalStrike(player)) { + if(warned) { embed.setDescription(String.format( - "S%d (Max) ・ Mute Duration: %s", - PenaltyConfig.getPlayerStrike(player), - PenaltyConfig.getAutoMuteDuration(player) + "Warning: %d", + PenaltyConfig.getPlayerWarnings(player) + 1 )); - } else { + } + else { embed.setDescription(String.format( - "S%d ► S%d ・ Mute Duration: %s", - PenaltyConfig.getPlayerStrike(player), - PenaltyConfig.getPlayerStrike(player) + 1, - PenaltyConfig.getAutoMuteDuration(player) + "Strike: %d - Mute Duration: %s", + PenaltyConfig.getPlayerStrike(player) + severity, + PenaltyConfig.getAutoMuteDuration(player) )); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/DiscordEmbed.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/DiscordEmbed.java index 92e59bf..c422665 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/DiscordEmbed.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/DiscordEmbed.java @@ -36,7 +36,7 @@ protected void setupBaseEmbed() { } embed.setFooter( - String.format("ChatGuard v%s ・ Logger", pluginVersion), + String.format("ChatGuard v%s - Logger", pluginVersion), "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" ); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/NameEmbed.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/NameEmbed.java index 9c4ed84..e9cdf08 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/NameEmbed.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/NameEmbed.java @@ -11,10 +11,12 @@ public final class NameEmbed extends DiscordEmbed { private final String trigger; + private final int severity; - public NameEmbed(JavaPlugin plugin, Player player, String content, String trigger) { + public NameEmbed(JavaPlugin plugin, Player player, String content, String trigger, int severity) { super(plugin, player, content); this.trigger = trigger; + this.severity = severity; setupBaseEmbed(); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/SignEmbed.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/SignEmbed.java index 0f995d4..d56bd6e 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/SignEmbed.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/embed/SignEmbed.java @@ -11,27 +11,21 @@ public final class SignEmbed extends DiscordEmbed { private final String trigger; + private final int severity; - public SignEmbed(JavaPlugin plugin, Player player, String content, String trigger) { + public SignEmbed(JavaPlugin plugin, Player player, String content, String trigger, int severity) { super(plugin, player, content); this.trigger = trigger; + this.severity = severity; setupBaseEmbed(); } @Override protected void setupEmbedDetails() { - if (PenaltyConfig.isPlayerOnFinalStrike(player)) { - embed.setDescription(String.format( - "S%d (Max)", - PenaltyConfig.getPlayerStrike(player) - )); - } else { - embed.setDescription(String.format( - "S%d ► S%d", - PenaltyConfig.getPlayerStrike(player), - PenaltyConfig.getPlayerStrike(player) + 1 - )); - } + embed.setDescription(String.format( + "Strike: %d", + PenaltyConfig.getPlayerStrike(player) + 1 + )); embed.setTitle("Sign Filter") .addField("Content:", content, false) diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/ConsoleLogger.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/ConsoleLogger.java index bab5ac0..0ef1bbc 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/ConsoleLogger.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/ConsoleLogger.java @@ -4,7 +4,6 @@ import io.github.aleksandarharalanov.chatguard.core.config.FilterConfig; import io.github.aleksandarharalanov.chatguard.core.log.LogAttribute; import io.github.aleksandarharalanov.chatguard.core.log.LogType; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import org.bukkit.entity.Player; public final class ConsoleLogger { @@ -32,7 +31,7 @@ public static void log(LogType logType, Player player, String content) { return; } - LogUtil.logConsoleInfo(logMessage); + System.out.println(logMessage); } private static boolean shouldConsoleLogEnabled(LogType logType) { diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/DiscordLogger.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/DiscordLogger.java index 9dee813..54b7c96 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/DiscordLogger.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/log/logger/DiscordLogger.java @@ -2,10 +2,10 @@ import io.github.aleksandarharalanov.chatguard.ChatGuard; import io.github.aleksandarharalanov.chatguard.core.config.DiscordConfig; +import io.github.aleksandarharalanov.chatguard.core.config.FilterTerm; import io.github.aleksandarharalanov.chatguard.core.log.LogType; import io.github.aleksandarharalanov.chatguard.core.log.embed.*; import io.github.aleksandarharalanov.chatguard.util.log.DiscordUtil; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -15,7 +15,12 @@ public final class DiscordLogger { private DiscordLogger() {} - public static void log(LogType logType, Player player, String content, String trigger) { + public static void log(LogType logType, Player player, String content) { + log(logType, player, content, null, false); + } + + public static void log(LogType logType, Player player, String content, FilterTerm triggerFilter, boolean warned) + { if (!DiscordConfig.getDiscordLogEnabled(logType)) { return; } @@ -28,22 +33,31 @@ public static void log(LogType logType, Player player, String content, String tr webhook.setUsername(webhookName); webhook.setAvatarUrl(webhookIcon); + + String trigger = ""; + int severity = 0; + + if(triggerFilter != null) { + trigger = triggerFilter.getName(); + severity = triggerFilter.getSeverity(); + } + DiscordEmbed embed; switch (logType) { case CHAT: - embed = new ChatEmbed(ChatGuard.getInstance(), player, content, trigger); + embed = new ChatEmbed(ChatGuard.getInstance(), player, content, trigger, severity, warned); break; case SIGN: - embed = new SignEmbed(ChatGuard.getInstance(), player, content, trigger); + embed = new SignEmbed(ChatGuard.getInstance(), player, content, trigger, severity); break; case NAME: - embed = new NameEmbed(ChatGuard.getInstance(), player, content, trigger); + embed = new NameEmbed(ChatGuard.getInstance(), player, content, trigger, severity); break; case CAPTCHA: embed = new CaptchaEmbed(ChatGuard.getInstance(), player, content); break; default: - LogUtil.logConsoleWarning("[ChatGuard] Something went wrong when constructing webhook embed to log."); + System.out.println("[ChatGuard] Something went wrong when constructing webhook embed to log."); return; } webhook.addEmbed(embed.getEmbed()); @@ -52,7 +66,7 @@ public static void log(LogType logType, Player player, String content, String tr try { webhook.execute(); } catch (IOException e) { - LogUtil.logConsoleWarning(e.getMessage()); + System.out.println(e.getMessage()); } }, 1L); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/captcha/CaptchaHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/captcha/CaptchaHandler.java index f6e290c..31afc56 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/captcha/CaptchaHandler.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/captcha/CaptchaHandler.java @@ -38,7 +38,7 @@ public static void processCaptchaTrigger(Player player, String content) { AudioCuePlayer.play(LogType.CAPTCHA, player, false); ConsoleLogger.log(LogType.CAPTCHA, player, content); - DiscordLogger.log(LogType.CAPTCHA, player, content, null); + DiscordLogger.log(LogType.CAPTCHA, player, content); for (Player p : Bukkit.getServer().getOnlinePlayers()) { if (AccessUtil.senderHasPermission(p, "chatguard.captcha")) { diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/common/ContentHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/common/ContentHandler.java index 1bdfbfe..a8c53bb 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/common/ContentHandler.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/common/ContentHandler.java @@ -1,7 +1,5 @@ package io.github.aleksandarharalanov.chatguard.core.security.common; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; - import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,13 +23,39 @@ public static String sanitizeContent(String content, List termsWhitelist Matcher matcher = pattern.matcher(sanitizedContent); sanitizedContent = matcher.replaceAll(""); } catch (RuntimeException e) { - LogUtil.logConsoleWarning(String.format("[ChatGuard] Invalid regex pattern '%s' in config: %s", regex, e.getMessage())); + System.out.println(String.format("[ChatGuard] Invalid regex pattern '%s' in config: %s", regex, e.getMessage())); } } return sanitizedContent.trim(); } + public static String sanitizeContent(String content, List whiteList) { + String sanitizedContent = content.toLowerCase(); + sanitizedContent = sanitizedContent.trim(); + sanitizedContent = decolorize(sanitizedContent); + + for (String regex : whiteList) { + try { + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(sanitizedContent); + sanitizedContent = matcher.replaceAll(""); + } catch (RuntimeException e) { + System.out.println(String.format("[ChatGuard] Invalid regex pattern '%s' in config: %s", regex, e.getMessage())); + } + } + + return sanitizedContent.trim(); + } + + private static String decolorize(String input) { + // there is a method for this already in ChatColor for bukkit, but it only removes § and not & + input = input.replaceAll("(?i)§[0-F]", ""); + input = input.replaceAll("(?i)&[0-F]", ""); + + return input; + } + public static String mergeContent(String[] content) { return Stream.of(content) .map(String::toLowerCase) diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterDetector.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterDetector.java index 17a5363..a582198 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterDetector.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterDetector.java @@ -1,7 +1,7 @@ package io.github.aleksandarharalanov.chatguard.core.security.filter; import io.github.aleksandarharalanov.chatguard.core.config.FilterConfig; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; +import io.github.aleksandarharalanov.chatguard.core.config.FilterTerm; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -10,31 +10,24 @@ public final class FilterDetector { private FilterDetector() {} - public static String getTrigger(String sanitizedContent) { - String trigger = checkBlacklistedTerms(sanitizedContent); - return (trigger != null) ? trigger : checkRegexPatterns(sanitizedContent); - } + public static FilterTrigger checkFilters(String sanitizedContent) { + for (FilterTerm filter : FilterConfig.getBlacklist()) { + String regex = filter.getFilter(); - private static String checkBlacklistedTerms(String sanitizedContent) { - String[] contentTerms = sanitizedContent.split("\\s+"); - for (String term : contentTerms) { - if (FilterConfig.getTermsBlacklist().contains(term)) { - return term; - } - } - return null; - } - - private static String checkRegexPatterns(String sanitizedContent) { - for (String regex : FilterConfig.getRegexBlacklist()) { try { Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(sanitizedContent); + if (matcher.find()) { - return regex.replace("\\", "\\\\").replace("\"", "\\\""); + final String flaggedSection = matcher.group(); + + final String cleanedFilter = regex.replace("\\", "\\\\").replace("\"", "\\\""); + final FilterTerm filterTerm = new FilterTerm(filter.getName(), cleanedFilter, filter.getSeverity()); + + return new FilterTrigger(flaggedSection, filterTerm); } } catch (RuntimeException e) { - LogUtil.logConsoleWarning(String.format("[ChatGuard] Invalid regex pattern '%s' in config: %s", regex, e.getMessage())); + System.out.println(String.format("[ChatGuard] Invalid regex pattern '%s' in config: %s", regex, e.getMessage())); } } return null; diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterFinalizer.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterFinalizer.java index 54bff03..a1e687b 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterFinalizer.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterFinalizer.java @@ -1,6 +1,8 @@ package io.github.aleksandarharalanov.chatguard.core.security.filter; import io.github.aleksandarharalanov.chatguard.core.config.FilterConfig; +import io.github.aleksandarharalanov.chatguard.core.config.FilterTerm; +import io.github.aleksandarharalanov.chatguard.core.config.PenaltyConfig; import io.github.aleksandarharalanov.chatguard.core.log.LogAttribute; import io.github.aleksandarharalanov.chatguard.core.log.LogType; import io.github.aleksandarharalanov.chatguard.core.log.logger.ConsoleLogger; @@ -15,28 +17,54 @@ public final class FilterFinalizer { private FilterFinalizer() {} - public static void finalizeActions(LogType logType, Player player, String content, String trigger) { - if (shouldWarnPlayer(logType)) { - player.sendMessage(ColorUtil.translateColorCodes(getWarningMessage(logType))); - } + public static void finalizeActions(LogType logType, Player player, String content, FilterTrigger trigger) { + final FilterTerm filterTerm = trigger.getFilterTerm(); + final int severity = filterTerm.getSeverity(); + final boolean shouldWarn = shouldWarn(logType, player, severity); AudioCuePlayer.play(logType, player, false); ConsoleLogger.log(logType, player, content); FileLogger.log(logType, player, content); - DiscordLogger.log(logType, player, content, trigger); + DiscordLogger.log(logType, player, content, filterTerm, shouldWarn); + + if (shouldSendFeedback(logType)) { + final String badWord = filterTerm.getName(); + final String flaggedSection = trigger.getSection(); + + player.sendMessage(ColorUtil.translateColorCodes( + String.format("&cYour message contains the flagged word: '%s'.", badWord) + )); + player.sendMessage(ColorUtil.translateColorCodes( + String.format("&cSection flagged: '%s'.", flaggedSection) + )); + } + + if (shouldWarn) { + PenaltyEnforcer.handleWarning(player); + return; // prevent the mute from being issued + } + + PenaltyEnforcer.incrementStrikeTier(logType, player, severity); PenaltyEnforcer.processMute(logType, player); - PenaltyEnforcer.incrementStrikeTier(logType, player); } - private static boolean shouldWarnPlayer(LogType logType) { - return logType.hasAttribute(LogAttribute.WARN) && FilterConfig.getWarnPlayerEnabled(); + private static boolean shouldWarn(LogType logType, Player player, int severity) { + if (!logType.hasAttribute(LogAttribute.MUTE)) + return false; + + final int warningBypassThreashold = FilterConfig.getWarningBypassThreashold(); + if(severity >= warningBypassThreashold) + return false; + + final int playerWarnings = PenaltyConfig.getPlayerWarnings(player); + final int maxWarnings = FilterConfig.getWarningcount(); + if(playerWarnings >= maxWarnings) + return false; + + return true; } - private static String getWarningMessage(LogType logType) { - if (logType == LogType.SIGN) { - return "&cSign censored due to bad words."; - } else { - return "&cMessage cancelled due to bad words."; - } + private static boolean shouldSendFeedback(LogType logType) { + return logType.hasAttribute(LogAttribute.WARN) && FilterConfig.getWarnPlayerEnabled(); } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterHandler.java index c40bc08..8ca3185 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterHandler.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterHandler.java @@ -22,8 +22,8 @@ public static boolean isPlayerNameBlocked(Player player) { } private static boolean isBlocked(LogType logType, Player player, String content) { - String sanitizedContent = ContentHandler.sanitizeContent(content, FilterConfig.getTermsWhitelist(), FilterConfig.getRegexWhitelist()); - String trigger = FilterDetector.getTrigger(sanitizedContent); + String sanitizedContent = ContentHandler.sanitizeContent(content, FilterConfig.getWhitelist()); + FilterTrigger trigger = FilterDetector.checkFilters(sanitizedContent); if (trigger == null) { return false; diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterTrigger.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterTrigger.java new file mode 100644 index 0000000..7285dda --- /dev/null +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/FilterTrigger.java @@ -0,0 +1,21 @@ +package io.github.aleksandarharalanov.chatguard.core.security.filter; + +import io.github.aleksandarharalanov.chatguard.core.config.FilterTerm; + +public class FilterTrigger { + private final String section; + private final FilterTerm filterTerm; + + public FilterTrigger(String section, FilterTerm filterTerm) { + this.section = section; + this.filterTerm = filterTerm; + } + + public String getSection() { + return section; + } + + public FilterTerm getFilterTerm() { + return filterTerm; + } +} diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/PenaltyEnforcer.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/PenaltyEnforcer.java index 87ee22b..ffc5f3c 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/PenaltyEnforcer.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/PenaltyEnforcer.java @@ -8,7 +8,6 @@ import io.github.aleksandarharalanov.chatguard.core.security.common.TimeFormatter; import io.github.aleksandarharalanov.chatguard.core.security.penalty.plugin.EssentialsMuteHandler; import io.github.aleksandarharalanov.chatguard.core.security.penalty.plugin.ZCoreMuteHandler; -import io.github.aleksandarharalanov.chatguard.util.log.LogUtil; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -33,7 +32,7 @@ private PenaltyEnforcer() {} public static void processMute(LogType logType, Player player) { if (muteHandler == null) { - LogUtil.logConsoleWarning("[ChatGuard] No compatible plugin found for auto mute feature. Please disable in config."); + System.out.println("[ChatGuard] No compatible plugin found for auto mute feature. Please disable in config."); return; } @@ -46,11 +45,13 @@ public static void processMute(LogType logType, Player player) { } try { - muteHandler.setPlayerMuteTimeout( - player.getName(), - TimeFormatter.parseDateDiff(PenaltyConfig.getAutoMuteDuration(player), true)); + String duration = PenaltyConfig.getAutoMuteDuration(player); + + long timeStamp = TimeFormatter.parseDateDiff(duration, true); + + muteHandler.setPlayerMuteTimeout(player.getName(), timeStamp); } catch (Exception e) { - LogUtil.logConsoleSevere(e.getMessage()); + e.printStackTrace(); return; } @@ -60,15 +61,42 @@ public static void processMute(LogType logType, Player player) { ))); } - public static void incrementStrikeTier(LogType logType, Player player) { + public static void incrementStrikeTier(LogType logType, Player player, int severity) { if (!logType.hasAttribute(LogAttribute.STRIKE)) { return; } - PenaltyConfig.incrementPlayerStrike(player); + PenaltyConfig.incrementPlayerStrike(player, severity); } public static IMuteHandler getMuteHandler() { return muteHandler; } + + public static void updatePlayerStrikes(Player player) { + if(!FilterConfig.getStrikeDecayEnabled()) + return; + + final long lastMuteTime = PenaltyConfig.getLastMuteTime(player); + if(lastMuteTime == -1) // player has no strikes + return; + + final long timePassed = System.currentTimeMillis() - lastMuteTime; + final long decayPeriod = FilterConfig.getStrikeDecayPeriod(); + + final int strikesToRevoke = (int) (timePassed / decayPeriod); + + // this is only really done to prevent longer decay times than would normally happen under the config + final long totalRevokePeriod = strikesToRevoke * decayPeriod; + final long newPlayerUpdatedTime = lastMuteTime + totalRevokePeriod; + + PenaltyConfig.decrementPlayerStrike(player, strikesToRevoke, newPlayerUpdatedTime); + } + + public static void handleWarning(Player player) { + PenaltyConfig.incrementPlayerWarnings(player); + + final String coloredMessage = ColorUtil.translateColorCodes("&cWarning number: " + PenaltyConfig.getPlayerWarnings(player)); + player.sendMessage(coloredMessage); + } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerJoinListener.java b/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerJoinListener.java index 2fed789..eb1de18 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerJoinListener.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerJoinListener.java @@ -2,7 +2,7 @@ import io.github.aleksandarharalanov.chatguard.core.config.FilterConfig; import io.github.aleksandarharalanov.chatguard.core.security.filter.FilterHandler; -import io.github.aleksandarharalanov.chatguard.core.config.PenaltyConfig; +import io.github.aleksandarharalanov.chatguard.core.security.penalty.PenaltyEnforcer; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerJoinEvent; @@ -20,6 +20,6 @@ public void onPlayerJoin(PlayerJoinEvent event) { return; } - PenaltyConfig.setDefaultStrikeTier(player); + PenaltyEnforcer.updatePlayerStrikes(player); } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 15c4af4..4a1d2b7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -34,11 +34,28 @@ filter: local-file: true auto-mute: enabled: true - duration: ["30m", "1h", "2h", "4h", "8h", "24h"] + duration: + - 10s + - 2m + - 10m + - 30m + - 30m + - 30m + - 1h + - 1h + - 1h + - 2h + - 4h + - 8h + - 24h + strike-decay: + enabled: true + period: 1w + warnings: + warning-count: 3 + severity-bypass-threshold: 3 rules: - whitelist: - terms: [] - regex: [] + whitelist: [] blacklist: - terms: [] - regex: [] \ No newline at end of file + - [name, \bexample filter\b, 2] + - [anothername, \banother example\b] \ No newline at end of file