diff --git a/build.gradle b/build.gradle index c7b9c6f..cbd7b0a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,11 +6,13 @@ plugins { repositories { mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { implementation files("libs/craftbukkit-1060.jar") implementation files("libs/Essentials.jar") + implementation 'com.github.zavdav:ZCore:1.0.0-rc1' } File ymlFile = file('src/main/resources/plugin.yml') as File diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/AboutCommand.java b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/AboutCommand.java index 2e5b34e..4b6abe2 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/AboutCommand.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/command/subcommand/AboutCommand.java @@ -21,7 +21,8 @@ public AboutCommand(ChatGuard plugin) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // Contributors: If you've contributed code, you can add your name here to be credited List contributors = Arrays.asList( - "moderator_man" + "moderator_man", + "zavdav" ); AboutUtil.aboutPlugin(sender, plugin, contributors); return true; diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/data/PenaltyData.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/data/PenaltyData.java index 6f5f729..35ee3f6 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/data/PenaltyData.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/data/PenaltyData.java @@ -17,14 +17,14 @@ public static int getStrike(Player player) { public static String getMuteDuration(String playerName) { return ChatGuard.getConfig().getString(String.format( - "filter.essentials-mute.duration.s%d", + "filter.auto-mute.duration.s%d", ChatGuard.getStrikes().getInt(playerName, 0) )); } public static String getMuteDuration(Player player) { return ChatGuard.getConfig().getString(String.format( - "filter.essentials-mute.duration.s%d", + "filter.auto-mute.duration.s%d", ChatGuard.getStrikes().getInt(player.getName(), 0) )); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/misc/TimeFormatter.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/misc/TimeFormatter.java index 93f7616..de22210 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/misc/TimeFormatter.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/misc/TimeFormatter.java @@ -1,18 +1,24 @@ package io.github.aleksandarharalanov.chatguard.core.misc; -import com.earth2me.essentials.User; +import io.github.aleksandarharalanov.chatguard.core.security.penalty.MuteEnforcer; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.LinkedHashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public final class TimeFormatter { private TimeFormatter() {} - public static void printFormattedMuteDuration(User user) { - long remainingMillis = user.getMuteTimeout() - System.currentTimeMillis(); + public static void printFormattedMuteDuration(String username) { + long remainingMillis = MuteEnforcer.muteHandler.getUserMuteTimeout(username) - System.currentTimeMillis(); Map timeUnits = new LinkedHashMap<>(); timeUnits.put("d.", (int) (remainingMillis / (1000 * 60 * 60 * 24))); @@ -25,8 +31,91 @@ public static void printFormattedMuteDuration(User user) { .map(entry -> entry.getValue() + entry.getKey()) .collect(Collectors.joining(" ", "", "")); - user.sendMessage(ColorUtil.translateColorCodes(String.format( + Player player = Bukkit.getServer().getPlayer(username); + player.sendMessage(ColorUtil.translateColorCodes(String.format( "&7Expires in %s", formattedTime ))); } + + // Essentials code: com.earth2me.essentials.Util.parseDateDiff + public static long parseDateDiff(String time, boolean future) throws Exception { + Pattern timePattern = Pattern.compile( + "(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); + Matcher m = timePattern.matcher(time); + int years = 0; + int months = 0; + int weeks = 0; + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + boolean found = false; + while (m.find()) { + if (m.group() == null || m.group().isEmpty()) { + continue; + } + for (int i = 0; i < m.groupCount(); i++) { + if (m.group(i) != null && !m.group(i).isEmpty()) { + found = true; + break; + } + } + if (found) { + if (m.group(1) != null && !m.group(1).isEmpty()) { + years = Integer.parseInt(m.group(1)); + } + if (m.group(2) != null && !m.group(2).isEmpty()) { + months = Integer.parseInt(m.group(2)); + } + if (m.group(3) != null && !m.group(3).isEmpty()) { + weeks = Integer.parseInt(m.group(3)); + } + if (m.group(4) != null && !m.group(4).isEmpty()) { + days = Integer.parseInt(m.group(4)); + } + if (m.group(5) != null && !m.group(5).isEmpty()) { + hours = Integer.parseInt(m.group(5)); + } + if (m.group(6) != null && !m.group(6).isEmpty()) { + minutes = Integer.parseInt(m.group(6)); + } + if (m.group(7) != null && !m.group(7).isEmpty()) { + seconds = Integer.parseInt(m.group(7)); + } + break; + } + } + if (!found) { + throw new Exception(); + } + Calendar c = new GregorianCalendar(); + if (years > 0) { + c.add(Calendar.YEAR, years * (future ? 1 : -1)); + } + if (months > 0) { + c.add(Calendar.MONTH, months * (future ? 1 : -1)); + } + if (weeks > 0) { + c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); + } + if (days > 0) { + c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); + } + if (hours > 0) { + c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); + } + if (minutes > 0) { + c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); + } + if (seconds > 0) { + c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); + } + return c.getTimeInMillis(); + } } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/ContentHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/ContentHandler.java index 71a814d..2e5d70c 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/ContentHandler.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/filter/ContentHandler.java @@ -24,7 +24,7 @@ public static void handleBlockedContent(LogType logType, Player player, String c ConsoleLogger.log(logType, player, content); FileLogger.log(logType, player, content); DiscordLogger.log(logType, player, content, trigger); - MuteEnforcer.processEssentialsMute(logType, player); + MuteEnforcer.processMute(logType, player); StrikeEnforcer.incrementStrikeTier(logType, player); } diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/EssentialsMuteHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/EssentialsMuteHandler.java new file mode 100644 index 0000000..a4a4fa2 --- /dev/null +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/EssentialsMuteHandler.java @@ -0,0 +1,32 @@ +package io.github.aleksandarharalanov.chatguard.core.security.penalty; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.User; + +public class EssentialsMuteHandler implements MuteHandler { + + private final Essentials essentials; + + public EssentialsMuteHandler(Essentials essentials) { + this.essentials = essentials; + } + + @Override + public boolean isUserMuted(String username) { + User user = essentials.getUser(username); + return user.isMuted(); + } + + @Override + public long getUserMuteTimeout(String username) { + User user = essentials.getUser(username); + return user.getMuteTimeout(); + } + + @Override + public void setUserMuteTimeout(String username, long timestamp) { + User user = essentials.getUser(username); + user.setMuteTimeout(timestamp); + user.setMuted(true); + } +} diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteEnforcer.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteEnforcer.java index 721acda..dc76023 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteEnforcer.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteEnforcer.java @@ -1,36 +1,49 @@ package io.github.aleksandarharalanov.chatguard.core.security.penalty; import com.earth2me.essentials.Essentials; -import com.earth2me.essentials.User; -import com.earth2me.essentials.Util; import io.github.aleksandarharalanov.chatguard.ChatGuard; import io.github.aleksandarharalanov.chatguard.core.data.PenaltyData; import io.github.aleksandarharalanov.chatguard.core.log.LogAttribute; import io.github.aleksandarharalanov.chatguard.core.log.LogType; +import io.github.aleksandarharalanov.chatguard.core.misc.TimeFormatter; import io.github.aleksandarharalanov.chatguard.util.misc.ColorUtil; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; public final class MuteEnforcer { + public static final MuteHandler muteHandler; + + static { + PluginManager pM = Bukkit.getServer().getPluginManager(); + if (pM.getPlugin("Essentials") != null) { + muteHandler = new EssentialsMuteHandler((Essentials) pM.getPlugin("Essentials")); + } else if (pM.getPlugin("ZCore") != null) { + muteHandler = new ZCoreMuteHandler(); + } else { + muteHandler = null; + } + } + private MuteEnforcer() {} - public static void processEssentialsMute(LogType logType, Player player) { - boolean isEssentialsMuteEnabled = ChatGuard.getConfig().getBoolean("filter.essentials-mute.enabled", true); - if (!isEssentialsMuteEnabled) return; + public static void processMute(LogType logType, Player player) { + boolean isAutomaticMuteEnabled = ChatGuard.getConfig().getBoolean("filter.auto-mute.enabled", true); + if (!isAutomaticMuteEnabled) return; if (!logType.hasAttribute(LogAttribute.MUTE) || logType == LogType.NAME) return; - Essentials essentials = (Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials"); - if (essentials == null || !essentials.isEnabled()) return; + if (muteHandler == null) return; - User user = essentials.getUser(player.getName()); try { - user.setMuteTimeout(Util.parseDateDiff(PenaltyData.getMuteDuration(player), true)); + muteHandler.setUserMuteTimeout( + player.getName(), + TimeFormatter.parseDateDiff(PenaltyData.getMuteDuration(player), true)); } catch (Exception e) { e.printStackTrace(); + return; } - user.setMuted(true); Bukkit.getServer().broadcastMessage(ColorUtil.translateColorCodes(String.format( "&c[ChatGuard] %s muted for %s. by system; content has bad words.", diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteHandler.java new file mode 100644 index 0000000..4047aef --- /dev/null +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/MuteHandler.java @@ -0,0 +1,10 @@ +package io.github.aleksandarharalanov.chatguard.core.security.penalty; + +public interface MuteHandler { + + boolean isUserMuted(String username); + + long getUserMuteTimeout(String username); + + void setUserMuteTimeout(String username, long timestamp); +} diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/ZCoreMuteHandler.java b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/ZCoreMuteHandler.java new file mode 100644 index 0000000..e696227 --- /dev/null +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/core/security/penalty/ZCoreMuteHandler.java @@ -0,0 +1,41 @@ +package io.github.aleksandarharalanov.chatguard.core.security.penalty; + +import me.zavdav.zcore.api.Punishments; +import me.zavdav.zcore.data.Mute; +import me.zavdav.zcore.util.PlayerUtils; + +import java.util.UUID; + +public class ZCoreMuteHandler implements MuteHandler { + + @Override + public boolean isUserMuted(String username) { + return Punishments.isPlayerMuted(PlayerUtils.getUUIDFromUsername(username)); + } + + @Override + public long getUserMuteTimeout(String username) { + UUID uuid = PlayerUtils.getUUIDFromUsername(username); + if (!Punishments.isPlayerMuted(uuid)) { + return System.currentTimeMillis(); + } + + Mute mute = Punishments.getMute(uuid); + + // It is possible that getDuration() returns null, which specifies a permanent mute. + // In this case, return 100 years as the timeout. + if (mute.getDuration() == null) { + return 31_536_000L * 1000 * 100; + } else { + return mute.getDuration() * 1000; + } + } + + @Override + public void setUserMuteTimeout(String username, long timestamp) { + Punishments.mutePlayer(PlayerUtils.getUUIDFromUsername(username), + null, + (timestamp - System.currentTimeMillis()) / 1000, + "Bad words in chat message"); + } +} diff --git a/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerChatListener.java b/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerChatListener.java index 6d7616e..5a8b874 100644 --- a/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerChatListener.java +++ b/src/main/java/io/github/aleksandarharalanov/chatguard/listener/player/PlayerChatListener.java @@ -1,15 +1,13 @@ package io.github.aleksandarharalanov.chatguard.listener.player; -import com.earth2me.essentials.Essentials; -import com.earth2me.essentials.User; import io.github.aleksandarharalanov.chatguard.ChatGuard; import io.github.aleksandarharalanov.chatguard.core.misc.TimeFormatter; import io.github.aleksandarharalanov.chatguard.core.security.captcha.CaptchaDetector; import io.github.aleksandarharalanov.chatguard.core.security.captcha.CaptchaHandler; import io.github.aleksandarharalanov.chatguard.core.security.filter.ContentFilter; +import io.github.aleksandarharalanov.chatguard.core.security.penalty.MuteEnforcer; import io.github.aleksandarharalanov.chatguard.core.security.spam.ChatRateLimiter; import io.github.aleksandarharalanov.chatguard.util.auth.AccessUtil; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerListener; @@ -20,7 +18,7 @@ public class PlayerChatListener extends PlayerListener { public void onPlayerChat(PlayerChatEvent event) { Player player = event.getPlayer(); - if (isPlayerEssentialsMuted(player, event)) return; + if (isPlayerMuted(player, event)) return; if (hasBypassPermission(player)) return; if (handleActiveCaptchaVerification(player, event)) return; if (handleSpamPrevention(player, event)) return; @@ -28,15 +26,13 @@ public void onPlayerChat(PlayerChatEvent event) { if (handleCaptchaTriggerCheck(player, event)) return; } - private static boolean isPlayerEssentialsMuted(Player player, PlayerChatEvent event) { - Essentials essentials = (Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials"); - if (essentials != null && essentials.isEnabled()) { - User user = essentials.getUser(player.getName()); - if (user.isMuted()) { - TimeFormatter.printFormattedMuteDuration(user); - event.setCancelled(true); - return true; - } + private static boolean isPlayerMuted(Player player, PlayerChatEvent event) { + if (MuteEnforcer.muteHandler == null) return false; + + if (MuteEnforcer.muteHandler.isUserMuted(player.getName())) { + TimeFormatter.printFormattedMuteDuration(player.getName()); + event.setCancelled(true); + return true; } return false; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ab1553b..426d120 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -40,7 +40,7 @@ filter: log: console: true local-file: true - essentials-mute: + auto-mute: enabled: true duration: s0: "30m" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4ac3ce3..77cdf91 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,7 +4,7 @@ name: ChatGuard author: Beezle website: github.com/AleksandarHaralanov/ChatGuard description: Prevents messages, usernames, and signs containing blocked terms or matching RegEx patterns, enforces mutes, stops chat and command spam, prompts captcha verification, logs actions, and applies escalating penalties. -softdepend: [Essentials] +softdepend: [Essentials, ZCore] commands: chatguard: