diff --git a/src/main/java/io/github/thetrouper/sentinel/Sentinel.java b/src/main/java/io/github/thetrouper/sentinel/Sentinel.java index 8a64af1..fc1479b 100644 --- a/src/main/java/io/github/thetrouper/sentinel/Sentinel.java +++ b/src/main/java/io/github/thetrouper/sentinel/Sentinel.java @@ -12,6 +12,7 @@ import io.github.thetrouper.sentinel.events.CmdBlockEvents; import io.github.thetrouper.sentinel.events.CommandEvent; import io.github.thetrouper.sentinel.events.NBTEvents; import io.github.thetrouper.sentinel.server.functions.AntiSpam; +import io.github.thetrouper.sentinel.server.functions.ProfanityFilter; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; @@ -32,19 +33,7 @@ public final class Sentinel extends JavaPlugin { public static final PluginManager manager = Bukkit.getPluginManager(); public static String prefix = ""; public static final Logger log = Bukkit.getLogger(); - public static String webhook; - public static List trustedPlayers; - public static boolean blockSpecificCommands; - public static boolean preventNBT; - public static boolean logNBT; - public static boolean preventCmdBlocks; - public static boolean logCmdBlocks; - public static List dangerousCommands; - public static boolean logDangerousCommands; - public static List loggedCommands; - public static boolean deop; - public static boolean ban; - public static boolean reopCommand; + /** * Plugin startup logic */ @@ -56,10 +45,12 @@ public final class Sentinel extends JavaPlugin { saveDefaultConfig(); // Plugin startup logic - loadConfiguration(); + Config.loadConfiguration(); log.info("Sentinel has loaded! (" + getDescription().getVersion() + ")"); + // Enable Functions AntiSpam.enableAntiSpam(); + ProfanityFilter.enableAntiSwear(); prefix = Config.Plugin.getPrefix(); @@ -95,54 +86,15 @@ public final class Sentinel extends JavaPlugin { // Plugin shutdown logic log.info("Sentinel has disabled! (" + getDescription().getVersion() + ") Your server is now no longer protected!"); } - private void loadConfiguration() { - saveDefaultConfig(); - FileConfiguration config = getConfig(); - // Load prefix - prefix = config.getString("config.plugin.prefix"); - // Load webhook - webhook = config.getString("config.plugin.webhook"); - - // Load trusted players - trustedPlayers = config.getStringList("config.plugin.trusted"); - - // Load block-specific commands - blockSpecificCommands = config.getBoolean("config.plugin.block-specific"); - - // Load prevent NBT - preventNBT = config.getBoolean("config.plugin.prevent-nbt"); - - // Load log NBT - logNBT = config.getBoolean("config.plugin.log-nbt"); - - // Load prevent command blocks - preventCmdBlocks = config.getBoolean("config.plugin.prevent-cmdblocks"); - - // Load log command blocks - logCmdBlocks = config.getBoolean("config.plugin.log-cmdblocks"); - - // Load dangerous commands - dangerousCommands = config.getStringList("config.plugin.dangerous"); - - // Load log protected commands - logDangerousCommands = config.getBoolean("config.plugin.log-dangerous"); - - // Load logged commands - loggedCommands = config.getStringList("config.plugin.logged"); - - deop = config.getBoolean("config.plugin.deop"); - ban = config.getBoolean("config.plugin.ban"); - reopCommand = config.getBoolean("config.plugin.reop-command"); - } /** * Checks if a player is trusted. * @param player the player to check * @return true if the player is trusted, false otherwise */ public static boolean isTrusted(Player player) { - return trustedPlayers.contains(player.getUniqueId().toString()); + return Config.trustedPlayers.contains(player.getUniqueId().toString()); } /** @@ -151,7 +103,7 @@ public final class Sentinel extends JavaPlugin { * @return true if the command is logged, false otherwise */ public static boolean isLoggedCommand(String command) { - return loggedCommands.contains(command); + return Config.loggedCommands.contains(command); } /** @@ -160,7 +112,7 @@ public final class Sentinel extends JavaPlugin { * @return true if the command is dangerous, false otherwise */ public static boolean isDangerousCommand(String command) { - return dangerousCommands.contains(command); + return Config.dangerousCommands.contains(command); } /** * Returns an instance of this plugin diff --git a/src/main/java/io/github/thetrouper/sentinel/commands/InfoCommand.java b/src/main/java/io/github/thetrouper/sentinel/commands/InfoCommand.java index c9cd596..10a4b1b 100644 --- a/src/main/java/io/github/thetrouper/sentinel/commands/InfoCommand.java +++ b/src/main/java/io/github/thetrouper/sentinel/commands/InfoCommand.java @@ -7,7 +7,9 @@ package io.github.thetrouper.sentinel.commands; import io.github.thetrouper.sentinel.discord.WebhookSender; import io.github.thetrouper.sentinel.exceptions.CmdExHandler; import io.github.thetrouper.sentinel.server.functions.AntiSpam; +import io.github.thetrouper.sentinel.server.util.ServerUtils; import io.github.thetrouper.sentinel.server.util.TextUtils; +import org.bukkit.Bukkit; import org.bukkit.command.*; import java.util.ArrayList; @@ -38,6 +40,10 @@ public class InfoCommand implements TabExecutor { case "checkheat" -> { sender.sendMessage(TextUtils.prefix("Your heat is §e" + AntiSpam.heatMap.get(sender).toString())); } + case "dispatchtest" -> { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "say " + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting a dangerous command. Contact an administrator if you believe this to be a mistake."); + ServerUtils.sendCommand("say test complete!"); + } } return true; } catch (Exception ex) { @@ -54,7 +60,8 @@ public class InfoCommand implements TabExecutor { .add(1,new String[]{ "debugmode", "webhooktest", - "checkheat" + "checkheat", + "dispatchtest" }).build(); } } diff --git a/src/main/java/io/github/thetrouper/sentinel/commands/ReopCommand.java b/src/main/java/io/github/thetrouper/sentinel/commands/ReopCommand.java index 1ca0f87..700dcc4 100644 --- a/src/main/java/io/github/thetrouper/sentinel/commands/ReopCommand.java +++ b/src/main/java/io/github/thetrouper/sentinel/commands/ReopCommand.java @@ -1,6 +1,7 @@ package io.github.thetrouper.sentinel.commands; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.exceptions.CmdExHandler; import io.github.thetrouper.sentinel.server.util.TextUtils; import org.bukkit.command.Command; @@ -17,7 +18,7 @@ public class ReopCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { try { - if (Sentinel.reopCommand) { + if (Config.reopCommand) { String name = sender.getName().toString(); Player p = sender.getServer().getPlayer(name); if (Sentinel.isTrusted(p)) { diff --git a/src/main/java/io/github/thetrouper/sentinel/data/Config.java b/src/main/java/io/github/thetrouper/sentinel/data/Config.java index d3b24c6..aba263e 100644 --- a/src/main/java/io/github/thetrouper/sentinel/data/Config.java +++ b/src/main/java/io/github/thetrouper/sentinel/data/Config.java @@ -5,13 +5,17 @@ package io.github.thetrouper.sentinel.data; import io.github.thetrouper.sentinel.Sentinel; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Config loader */ public abstract class Config { - private static final FileConfiguration config = Sentinel.getInstance().getConfig(); /** @@ -22,4 +26,101 @@ public abstract class Config { return config.getString("config.plugin.prefix"); } } + public static String webhook; + public static List trustedPlayers; + public static boolean blockSpecificCommands; + public static boolean preventNBT; + public static boolean logNBT; + public static boolean preventCmdBlocks; + public static boolean logCmdBlocks; + public static List dangerousCommands; + public static boolean logDangerousCommands; + public static List loggedCommands; + public static boolean deop; + public static boolean ban; + public static boolean reopCommand; + + public static boolean antiSpamEnabled; + public static int defaultGain; + public static int lowGain; + public static int mediumGain; + public static int highGain; + public static int heatDecay; + public static int blockHeat; + public static int punishHeat; + public static String punishSpamCommand; + public static boolean logSpam; + public static boolean antiSwearEnabled; + public static int lowScore; + public static int mediumLowScore; + public static int mediumScore; + public static int mediumHighScore; + public static int highScore; + public static int punishScore; + public static String swearPunishCommand; + public static boolean slurInstaPunish; + public static String slurPunishCommand; + public static List swearWhitelist; + public static List swearBlacklist; + public static List slurs; + public static Map leetPatterns; + public static boolean logSwear; + + public static void loadConfiguration() { + + Sentinel.prefix = config.getString("config.plugin.prefix"); + // antiNuke + webhook = config.getString("config.plugin.webhook"); + trustedPlayers = config.getStringList("config.plugin.trusted"); + blockSpecificCommands = config.getBoolean("config.plugin.block-specific"); + preventNBT = config.getBoolean("config.plugin.prevent-nbt"); + logNBT = config.getBoolean("config.plugin.log-nbt"); + preventCmdBlocks = config.getBoolean("config.plugin.prevent-cmdblocks"); + logCmdBlocks = config.getBoolean("config.plugin.log-cmdblocks"); + dangerousCommands = config.getStringList("config.plugin.dangerous"); + logDangerousCommands = config.getBoolean("config.plugin.log-dangerous"); + loggedCommands = config.getStringList("config.plugin.logged"); + deop = config.getBoolean("config.plugin.deop"); + ban = config.getBoolean("config.plugin.ban"); + reopCommand = config.getBoolean("config.plugin.reop-command"); + // antiSpam + antiSpamEnabled = config.getBoolean("config.chat.anti-spam.enabled"); + defaultGain = config.getInt("config.chat.anti-spam.default-gain"); + lowGain = config.getInt("config.chat.anti-spam.low-gain"); + mediumGain = config.getInt("config.chat.anti-spam.medium-gain"); + highGain = config.getInt("config.chat.anti-spam.high-gain"); + heatDecay = config.getInt("config.chat.anti-spam.heat-decay"); + blockHeat = config.getInt("config.chat.anti-spam.block-heat"); + punishHeat = config.getInt("config.chat.anti-spam.punish-heat"); + punishSpamCommand = config.getString("config.chat.anti-spam.punish-command"); + logSpam = config.getBoolean("config.chat.anti-swear.log-swear"); + // antiSwear + antiSwearEnabled = config.getBoolean("config.chat.anti-swear.enabled"); + lowScore = config.getInt("config.chat.anti-swear.low-gain"); + mediumLowScore = config.getInt("config.chat.anti-swear.medium-low-gain"); + mediumScore = config.getInt("config.chat.anti-swear.medium-gain"); + mediumHighScore = config.getInt("config.chat.anti-swear.medium-high-gain"); + highScore = config.getInt("config.chat.anti-swear.high-gain"); + punishScore = config.getInt("config.chat.anti-swear.punish-score"); + swearPunishCommand = config.getString("config.chat.anti-swear.punish-command"); + slurInstaPunish = config.getBoolean("config.chat.anti-swear.slur-insta-punish"); + slurPunishCommand = config.getString("config.chat.anti-swear.slur-command"); + swearWhitelist = config.getStringList("config.chat.anti-swear.whitelisted"); + swearBlacklist = config.getStringList("config.chat.anti-swear.blacklisted"); + slurs = config.getStringList("config.chat.anti-swear.slurs"); + leetPatterns = loadLeetPatterns(); + logSwear = config.getBoolean("config.chat.anti-swear.log-swear"); + } + private static Map loadLeetPatterns() { + Map dictionary = new HashMap<>(); + ConfigurationSection section = config.getConfigurationSection("config.chat.anti-swear.leet-patterns"); + + if (section != null) { + for (String key : section.getKeys(false)) { + dictionary.put(key, section.getString(key)); + } + } + + return dictionary; + } } diff --git a/src/main/java/io/github/thetrouper/sentinel/data/Emojis.java b/src/main/java/io/github/thetrouper/sentinel/data/Emojis.java index 71908ac..15d05d0 100644 --- a/src/main/java/io/github/thetrouper/sentinel/data/Emojis.java +++ b/src/main/java/io/github/thetrouper/sentinel/data/Emojis.java @@ -1,9 +1,32 @@ package io.github.thetrouper.sentinel.data; public class Emojis { - public static String success = "<:success:1125240412081238066>"; + public static String rightSort = "<:rightSort:1125785837255270520>"; + public static String arrowRight = "<:arrowRight:1125785471520354304>"; + public static String rightDoubleArrow = "<:rightDoubleArrow:1125785800353783868>"; + public static String activity = "<:activity:1125785527468167178>"; + public static String alarm = "<:alarm:1125790301873770606>"; + public static String target = "<:target:1125788461371232307>"; + public static String bot = "<:bot:1125791121851826206>"; + public static String cancel = "<:cancel:1125785769471127694>"; + public static String creation = "<:creation:1125790610729730109>"; + public static String date = "<:date:1125790434443145297>"; + public static String kick = "<:kick:1125785612595761212>"; + public static String members = "<:members:1125791101199077426>"; + public static String mute = "<:mute:1125789032937435247>"; + public static String noDM = "<:noDM:1125790359423824022>"; + public static String owner = "<:owner:1125791175559876669>"; + public static String potentialDanger = "<:potentialDanger:1125788513971998741>"; + public static String roles = "<:roles:1125790513933594645>"; + public static String separator = "<:separator:1125790817626357861>"; + public static String splash = "<:splash:1125791213933563905>"; + public static String success = "<:success:1125785728161419356>"; + public static String suspicious = "<:suspicious:1125790709371371682>"; + public static String trustedAdmin = "<:trustedAdmin:1125785574591180822>"; + public static String upvoter = "<:upvoter:1125790659735977994>"; + public static String vanity = "<:vanity:1125791060594004039>"; + public static String webhook = "<:webhook:1125790545638330388>"; public static String failure = "<:failure:1125241087909429369>"; - public static String rightArrow = "<:rightArrow:1125241843597189160>"; public static String nuke = "<:nuke:1125244368807280702>"; public static String member = "<:member:1125244044407218176>"; } diff --git a/src/main/java/io/github/thetrouper/sentinel/discord/WebhookSender.java b/src/main/java/io/github/thetrouper/sentinel/discord/WebhookSender.java index 0c3ea9b..c0e9911 100644 --- a/src/main/java/io/github/thetrouper/sentinel/discord/WebhookSender.java +++ b/src/main/java/io/github/thetrouper/sentinel/discord/WebhookSender.java @@ -2,11 +2,13 @@ package io.github.thetrouper.sentinel.discord; import io.github.thetrouper.sentinel.Sentinel; import io.github.thetrouper.sentinel.commands.InfoCommand; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.data.Emojis; import io.github.thetrouper.sentinel.discord.DiscordWebhook; import io.github.thetrouper.sentinel.server.util.ServerUtils; import io.github.thetrouper.sentinel.server.util.TextUtils; import org.bukkit.block.Block; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import java.awt.Color; @@ -15,7 +17,7 @@ import java.io.IOException; public class WebhookSender { public static void sendHelloWorldEmbed() { - String webhookUrl = Sentinel.webhook; + String webhookUrl = Config.webhook; // Create a new DiscordWebhook instance DiscordWebhook webhook = new DiscordWebhook(webhookUrl); @@ -36,6 +38,7 @@ public class WebhookSender { } } + public static String successOrFail(boolean bool) { if (bool) { return Emojis.success; @@ -46,13 +49,13 @@ public class WebhookSender { public static void sendEmbedWarning(String player, String command, boolean denied, boolean removedOp, boolean banned) { ServerUtils.sendDebugMessage("Creating Command Webhook..."); final String description = - Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" + - Emojis.rightArrow + " **Command:** " + command + " " + Emojis.nuke + "\\n" + - Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" + - Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + - Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n"; + Emojis.arrowRight + " **Player:** " + player + " " + Emojis.member + "\\n" + + Emojis.arrowRight + " **Command:** " + command + " " + Emojis.nuke + "\\n" + + Emojis.arrowRight + " **Denied:** " + successOrFail(denied) + "\\n" + + Emojis.arrowRight + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + + Emojis.arrowRight + " **Banned:** " + successOrFail(banned) + "\\n"; - DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook); + DiscordWebhook webhook = new DiscordWebhook(Config.webhook); webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png"); webhook.setUsername("Sentinel Anti-Nuke | Logs"); DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() @@ -72,13 +75,13 @@ public class WebhookSender { public static void sendEmbedWarning(String player, Block b, boolean denied, boolean removedOp, boolean banned) { ServerUtils.sendDebugMessage("Creating Block Webhook..."); final String description = - Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" + - Emojis.rightArrow + " **Block:** " + b.getType() + " " + Emojis.nuke + "\\n" + - Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" + - Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + - Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n"; + Emojis.arrowRight + " **Player:** " + player + " " + Emojis.member + "\\n" + + Emojis.arrowRight + " **Block:** " + b.getType() + " " + Emojis.nuke + "\\n" + + Emojis.arrowRight + " **Denied:** " + successOrFail(denied) + "\\n" + + Emojis.arrowRight + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + + Emojis.arrowRight + " **Banned:** " + successOrFail(banned) + "\\n"; - DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook); + DiscordWebhook webhook = new DiscordWebhook(Config.webhook); webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png"); webhook.setUsername("Sentinel Anti-Nuke | Logs"); DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() @@ -99,13 +102,13 @@ public class WebhookSender { public static void sendEmbedWarning(String player, ItemStack item, boolean denied, boolean removedOp, boolean banned) { ServerUtils.sendDebugMessage("Creating Webhook..."); final String description = - Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" + - Emojis.rightArrow + " **Item:** " + item.getType() + " " + Emojis.nuke + "\\n" + - Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" + - Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + - Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n"; + Emojis.arrowRight + " **Player:** " + player + " " + Emojis.member + "\\n" + + Emojis.arrowRight + " **Item:** " + item.getType() + " " + Emojis.nuke + "\\n" + + Emojis.arrowRight + " **Denied:** " + successOrFail(denied) + "\\n" + + Emojis.arrowRight + " **Removed OP:** " + successOrFail(removedOp) + "\\n" + + Emojis.arrowRight + " **Banned:** " + successOrFail(banned) + "\\n"; - DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook); + DiscordWebhook webhook = new DiscordWebhook(Config.webhook); webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png"); webhook.setUsername("Sentinel Anti-Nuke | Logs"); DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() diff --git a/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java b/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java index 931b698..4fd8bd2 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java @@ -1,13 +1,17 @@ package io.github.thetrouper.sentinel.events; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.server.functions.AntiSpam; +import io.github.thetrouper.sentinel.server.functions.ProfanityFilter; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; public class ChatEvent implements Listener { + @EventHandler public static void onChat(AsyncPlayerChatEvent e) { - AntiSpam.handleAntiSpam(e); + if (Config.antiSwearEnabled) ProfanityFilter.handleProfanityFilter(e); + if (Config.antiSpamEnabled) AntiSpam.handleAntiSpam(e); } } diff --git a/src/main/java/io/github/thetrouper/sentinel/events/CmdBlockEvents.java b/src/main/java/io/github/thetrouper/sentinel/events/CmdBlockEvents.java index 7163375..31bece9 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/CmdBlockEvents.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/CmdBlockEvents.java @@ -1,6 +1,7 @@ package io.github.thetrouper.sentinel.events; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.server.util.DeniedActions; import org.bukkit.Material; import org.bukkit.block.Block; @@ -15,7 +16,7 @@ import org.bukkit.event.player.PlayerInteractEvent; public class CmdBlockEvents implements Listener { @EventHandler private void onCMDBlockUse(PlayerInteractEvent e) { - if (!Sentinel.preventCmdBlocks) return; + if (!Config.preventCmdBlocks) return; if (e.getClickedBlock() == null) return; Block b = e.getClickedBlock(); if (b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK) { @@ -28,7 +29,7 @@ public class CmdBlockEvents implements Listener { } @EventHandler private void onCMDBlockPlace(BlockPlaceEvent e) { - if (!Sentinel.preventCmdBlocks) return; + if (!Config.preventCmdBlocks) return; Block b = e.getBlockPlaced(); if (b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK ) { Player p = e.getPlayer(); @@ -40,7 +41,7 @@ public class CmdBlockEvents implements Listener { } @EventHandler private void onCMDBlockMinecartUse(PlayerInteractEntityEvent e) { - if (!Sentinel.preventCmdBlocks) return; + if (!Config.preventCmdBlocks) return; if (e.getRightClicked().getType() == EntityType.MINECART_COMMAND) { Player p = e.getPlayer(); if (!Sentinel.isTrusted(p)) { diff --git a/src/main/java/io/github/thetrouper/sentinel/events/CommandEvent.java b/src/main/java/io/github/thetrouper/sentinel/events/CommandEvent.java index 677c913..6c32466 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/CommandEvent.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/CommandEvent.java @@ -1,6 +1,7 @@ package io.github.thetrouper.sentinel.events; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.server.util.DeniedActions; import io.github.thetrouper.sentinel.server.util.ServerUtils; import io.github.thetrouper.sentinel.server.util.TextUtils; @@ -24,7 +25,7 @@ public class CommandEvent implements Listener { DeniedActions.handleDeniedAction(p,e.getMessage()); } } - if (Sentinel.blockSpecificCommands) { + if (Config.blockSpecificCommands) { ServerUtils.sendDebugMessage(TextUtils.prefix("Checking command for specific")); if (command.contains(":")) { ServerUtils.sendDebugMessage(TextUtils.prefix("Checking is specific")); diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/AntiSpam.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/AntiSpam.java index 28e1bd8..4c71309 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/AntiSpam.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/AntiSpam.java @@ -1,7 +1,13 @@ package io.github.thetrouper.sentinel.server.functions; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.server.util.GPTUtils; +import io.github.thetrouper.sentinel.server.util.ServerUtils; import io.github.thetrouper.sentinel.server.util.TextUtils; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -19,29 +25,58 @@ public class AntiSpam { heatMap = new HashMap<>(); lastMessageMap = new HashMap<>(); } - public static void handleAntiSpam(AsyncPlayerChatEvent event) { - Player player = event.getPlayer(); - String message = event.getMessage(); - if (!heatMap.containsKey(player)) heatMap.put(player, 0); - if (heatMap.get(player) > 10) { - event.setCancelled(true); - player.sendMessage(TextUtils.prefix("Rate limit exceeded! Please wait before sending another message.")); + public static void handleAntiSpam(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + String message = e.getMessage(); + if (!heatMap.containsKey(p)) heatMap.put(p, 0); + if (heatMap.get(p) > Config.punishHeat) { + e.setCancelled(true); + punishSpam(p,message, lastMessageMap.get(p)); return; } - if (lastMessageMap.containsKey(player)) { - String lastMessage = lastMessageMap.get(player); - double similarity = calculateSimilarity(message, lastMessage); - if (similarity > 0.5) heatMap.put(player, heatMap.get(player) + 3); - if (similarity > 0.9) heatMap.put(player, heatMap.get(player) + 6); + if (heatMap.get(p) > Config.blockHeat) { + e.setCancelled(true); + alertSpam(p, message, lastMessageMap.get(p)); + return; } - lastMessageMap.put(player, message); + if (lastMessageMap.containsKey(p)) { + String lastMessage = lastMessageMap.get(p); + double similarity = calculateSimilarity(message, lastMessage); + if (similarity > 0.25) heatMap.put(p, heatMap.get(p) + Config.lowGain); + if (similarity > 0.5) heatMap.put(p, heatMap.get(p) + Config.mediumGain); + if (similarity > 0.9) heatMap.put(p, heatMap.get(p) + Config.highGain); + } + lastMessageMap.put(p, message); } public static void decayHeat() { - for (Player player : heatMap.keySet()) { - int heat = heatMap.get(player); + for (Player p : heatMap.keySet()) { + int heat = heatMap.get(p); if (heat > 0) { - heatMap.put(player, heat - 1); + heat = heat - Config.heatDecay; + heatMap.put(p, Math.max(0, heat)); } } } + + public static void alertSpam(Player p, String message1, String message2) { + TextComponent text = new TextComponent(); + p.sendMessage(TextUtils.prefix("Do not spam in chat! Please wait before sending another message.")); + String hover = TextUtils.color("&bPrevious: &f" + message2 + "\n&bCurrent: &f" + message1 + "\n&bSimilarity &f" + GPTUtils.calculateSimilarity(message1,message2 + "%")); + text.setText(TextUtils.prefix(TextUtils.color + ("&b&n" + p.getName() + "&7 might be spamming! &8(&c" + heatMap.get(p) + "&7/&4" + Config.punishHeat + "&8)"))); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(hover))); + ServerUtils.forEachStaff(staff -> { + staff.spigot().sendMessage(text); + }); + } + public static void punishSpam(Player player, String message1, String message2) { + ServerUtils.sendCommand(Config.punishSpamCommand.replace("%player%", player.getName())); + player.sendMessage(TextUtils.prefix(TextUtils.color("&cYou have been auto-punished for violating the anti-spam repetitively!"))); + TextComponent text = new TextComponent(); + text.setText(TextUtils.prefix(TextUtils.color + ("&b&n" + player.getName() + "&7 has been auto-muted by the anti-spam! &8(&c" + heatMap.get(player) + "&7/&4" + Config.punishHeat + "&8)"))); + ServerUtils.forEachStaff(staff -> { + staff.spigot().sendMessage(text); + }); + } } diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java new file mode 100644 index 0000000..3df64fa --- /dev/null +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java @@ -0,0 +1,223 @@ +package io.github.thetrouper.sentinel.server.functions; +import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; +import io.github.thetrouper.sentinel.server.util.ServerUtils; +import io.github.thetrouper.sentinel.server.util.TextUtils; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ProfanityFilter { + public static Map scoreMap; + private static final List swearBlacklist = Config.swearBlacklist; + private static final List swearWhitelist = Config.swearWhitelist; + private static final List slurs = Config.slurs; + + public static void enableAntiSwear() { + scoreMap = new HashMap<>(); + } + public static void handleProfanityFilter(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + String message = e.getMessage(); + if (!scoreMap.containsKey(p)) scoreMap.put(p, 0); + if (scoreMap.get(p) > Config.punishScore) punishSwear(p,highlightProfanity(message),message); + switch (ProfanityFilter.checkSeverity(message)) { + case "low" -> { + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.lowScore); + p.sendMessage(TextUtils.prefix("§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute!")); + } + case "medium-low" -> { + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.mediumLowScore); + blockSwear(p,highlightProfanity(message),message); + } + case "medium" -> { + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.mediumScore); + blockSwear(p,highlightProfanity(message),message); + } + case "medium-high" -> { + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.mediumHighScore); + blockSwear(p,highlightProfanity(message),message); + } + case "high" -> { + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.highScore); + blockSwear(p,highlightProfanity(message),message); + } + case "slur" -> { + // Insta-Punish + e.setCancelled(true); + scoreMap.put(p, scoreMap.get(p) + Config.highScore); + punishSlur(p,highlightProfanity(message),message); + } + } + } + public static void punishSwear(Player player, String highlightedMSG, String origMessage) { + ServerUtils.sendCommand(Config.swearPunishCommand.replace("%player%", player.getName())); + player.sendMessage(TextUtils.prefix(TextUtils.color("&cYou have been auto-muted for violating the anti-swear repetitively!"))); + String hover = TextUtils.color("&bOriginal: &f" + origMessage + "\n&bSanitized: &f" + highlightedMSG + "\n&7&o(click to copy)"); + TextComponent text = new TextComponent(); + text.setText(TextUtils.prefix(TextUtils.color + ("&b&n" + player.getName() + "&7 has been auto-muted by the anti-swear! &8(&c" + scoreMap.get(player) + "&7/&4" + Config.punishScore + "&8)"))); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(hover))); + text.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, origMessage)); + + ServerUtils.forEachStaff(staff -> { + staff.spigot().sendMessage(text); + }); + } + public static void punishSlur(Player player, String highlightedMSG, String origMessage) { + if (!Config.slurInstaPunish) return; + ServerUtils.sendCommand(Config.slurPunishCommand.replace("%player%", player.getName())); + player.sendMessage(TextUtils.prefix(TextUtils.color("&cYou have been insta-muted for saying a slur!"))); + String hover = TextUtils.color("&bOriginal: &f" + origMessage + "\n&bSanitized: &f" + highlightedMSG + "\n&7&o(click to copy)"); + TextComponent text = new TextComponent(); + text.setText(TextUtils.prefix(TextUtils.color + ("&b&n" + player.getName() + "&7 has been insta-muted by the anti-swear! &8(&e" + scoreMap.get(player) + "&7/&4" + Config.punishScore + "&8)"))); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(hover))); + text.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, origMessage)); + + ServerUtils.forEachStaff(staff -> { + staff.spigot().sendMessage(text); + }); + } + public static void blockSwear(Player player, String highlightedMSG, String origMessage) { + player.sendMessage(TextUtils.prefix(TextUtils.color("&cPlease do not swear in chat! Attempting to bypass this filter will result in a mute!"))); + String hover = TextUtils.color("&bOriginal: &f" + origMessage + "\n&bSanitized: &f" + highlightedMSG + "\n&7&o(click to copy)"); + TextComponent text = new TextComponent(); + text.setText(TextUtils.prefix(TextUtils.color + ("&b&n" + player.getName() + "&7 has triggered the anti-swear! &8(&c" + scoreMap.get(player) + "&7/&4" + Config.punishScore + "&8)"))); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(hover))); + text.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, origMessage)); + + ServerUtils.forEachStaff(staff -> { + staff.spigot().sendMessage(text); + }); + } + + public static String highlightProfanity(String text) { + String lowercasedText = text.toLowerCase(); + String highlightedSwears = highlightSwears(lowercasedText, "§c", "§f"); + String highlightedText = highlightSlurs(highlightedSwears, "§e", "§f"); + return highlightedText; + } + + private static String highlightSwears(String text, String start, String end) { + for (String swear : swearBlacklist) { + if (text.contains(swear)) {text = text.replace(swear, start + swear + end);} + } + return text; + } + + private static String highlightSlurs(String text, String start, String end) { + for (String slur : slurs) { + if (text.contains(slur)) { + text = text.replace(slur, start + slur + end); + } + } + return text; + } + + /** + * 1: lowercase the text + * 2: remove the known false positives + * 3: Check for swears and return "low" if true + * 4: Convert LeetSpeak Characters + * 5: Check for swears and return "medium-low" if true + * 6: Strip all special characters + * 7: Check for swears and return "medium" if true + * 8: simplify repeating letters + * 9: Check for swears and return "medium-high" if true + * 10: remove periods and spaces + * 11: Check for swears and return "high" if true + */ + public static String checkSeverity(String text) { + // 1: + String lowercasedText = text.toLowerCase(); + + // 2: + String cleanedText = removeFalsePositives(lowercasedText); + + // 3: + if (containsSwears(cleanedText)) return "low"; + if (containsSlurs(cleanedText)) return "slur"; + + // 4: + String convertedText = convertLeetSpeakCharacters(cleanedText); + + // 5: + if (containsSwears(convertedText)) return "medium-low"; + if (containsSlurs(cleanedText)) return "slur"; + + // 6: + String strippedText = stripSpecialCharacters(convertedText); + + // 7: + if (containsSwears(strippedText)) return "medium"; + if (containsSlurs(strippedText)) return "slur"; + + // 8: + String simplifiedText = simplifyRepeatingLetters(strippedText); + + // 9: + if (containsSwears(simplifiedText)) return "medium-high"; + if (containsSlurs(simplifiedText)) return "slur"; + + // 10: + String finalText = removePeriodsAndSpaces(simplifiedText); + + // 11: + if (containsSwears(finalText)) return "high"; + if (containsSlurs(finalText)) return "slur"; + + return "safe"; + } + + private static String removeFalsePositives(String text) { + for (String falsePositive : swearWhitelist) { + text = text.replace(falsePositive, ""); + } + return text; + } + + private static boolean containsSwears(String text) { + for (String swear : swearBlacklist) { + if (text.contains(swear)) return true; + } + return false; + } + private static boolean containsSlurs(String text) { + for (String slur : slurs) { + if (text.contains(slur)) return true; + } + return false; + } + + private static String convertLeetSpeakCharacters(String text) { + text = TextUtils.fromLeetString(text); + return text; + } + + private static String stripSpecialCharacters(String text) { + text = text.replaceAll("[^A-Za-z0-9]", "").trim(); + return text; + } + + private static String simplifyRepeatingLetters(String text) { + text = TextUtils.replaceRepeatingLetters(text); + return text; + } + + private static String removePeriodsAndSpaces(String text) { + return text.replace(".", "").replace(" ", ""); + } +} diff --git a/src/main/java/io/github/thetrouper/sentinel/server/util/ArrayUtils.java b/src/main/java/io/github/thetrouper/sentinel/server/util/ArrayUtils.java new file mode 100644 index 0000000..2fb3351 --- /dev/null +++ b/src/main/java/io/github/thetrouper/sentinel/server/util/ArrayUtils.java @@ -0,0 +1,41 @@ +package io.github.thetrouper.sentinel.server.util; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +public final class ArrayUtils { + + /** + * Transforms an array to another one + * @param e iterable list + * @param a action + * @return new transformed list + * @param input + * @param output + */ + public static List toNewList(Iterable e, Function a) { + List list = new ArrayList<>(); + e.forEach(i -> list.add(a.apply(i))); + return list; + } + + public static String list2string(List list) { + return TextUtils.color("&7[&e" + String.join("&7, &e", ArrayUtils.toNewList(list, Object::toString)) + "&7]"); + } + + public static List bind(Iterable tList, T... ts) { + List list = Arrays.asList(ts); + tList.forEach(list::add); + return list; + } + + public static class Constants { + public static final List MATERIAL_NAMES = toNewList(Arrays.stream(Material.values()).toList(),m -> m.name().toLowerCase()); + public static final List ENTITY_NAMES = toNewList(Arrays.stream(EntityType.values()).toList(),e -> e.name().toLowerCase()); + } +} diff --git a/src/main/java/io/github/thetrouper/sentinel/server/util/DeniedActions.java b/src/main/java/io/github/thetrouper/sentinel/server/util/DeniedActions.java index 4d2bcde..725c997 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/util/DeniedActions.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/util/DeniedActions.java @@ -1,6 +1,7 @@ package io.github.thetrouper.sentinel.server.util; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.discord.WebhookSender; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.HoverEvent; @@ -18,15 +19,19 @@ public class DeniedActions { private static boolean opRemoved; private static boolean denied; + public static void logPunishment(Player p, String type, String reason) { + + } + public static void handleDeniedAction(Player p, String command) { ServerUtils.sendDebugMessage(TextUtils.prefix("Handling denied command...")); - if (!Sentinel.logDangerousCommands) return; + if (!Config.logDangerousCommands) return; ServerUtils.sendDebugMessage(TextUtils.prefix("LDC is enabled")); logMessage = "]==-- Sentinel --==[\n" + "A Dangerous command has been attempted!\n" + "Player: " + p.getName() + "\n" + "Command: " + command + "\n"; - if (Sentinel.deop) { + if (Config.deop) { ServerUtils.sendDebugMessage(TextUtils.prefix("Deoping player")); p.setOp(false); logMessage = logMessage + "Operator Removed: ✔\n"; @@ -35,7 +40,7 @@ public class DeniedActions { logMessage = logMessage + "Operator Removed: ✘\n"; opRemoved = false; } - if (Sentinel.ban) { + if (Config.ban) { ServerUtils.sendDebugMessage(TextUtils.prefix("Banning player")); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting a dangerous command. Contact an administrator if you believe this to be a mistake."); logMessage = logMessage + "Banned: ✔\n"; @@ -52,12 +57,12 @@ public class DeniedActions { WebhookSender.sendEmbedWarning(p.getName(),command,denied,opRemoved,banned); } public static void handleDeniedAction(Player p, Block block) { - if (!Sentinel.logCmdBlocks) return; + if (!Config.logCmdBlocks) return; logMessage = "]==-- Sentinel --==[\n" + "A Dangerous block usage has been detected!\n" + "Player: " + p.getName() + "\n" + "BlockType: " + block.getType() + "\n"; - if (Sentinel.deop) { + if (Config.deop) { p.setOp(false); logMessage = logMessage + "Operator Removed: ✔\n"; opRemoved = true; @@ -65,7 +70,7 @@ public class DeniedActions { logMessage = logMessage + "Operator Removed: ✘\n"; opRemoved = false; } - if (Sentinel.ban) { + if (Config.ban) { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting to use dangerous blocks. Contact an administrator if you believe this to be a mistake."); logMessage = logMessage + "Banned: ✔\n"; banned = true; @@ -80,12 +85,12 @@ public class DeniedActions { WebhookSender.sendEmbedWarning(p.getName(),block,denied,opRemoved,banned); } public static void handleDeniedAction(Player p, ItemStack i) { - if (!Sentinel.logNBT) return; + if (!Config.logNBT) return; logMessage = "]==-- Sentinel --==[\n" + "A Dangerous item has been detected!\n" + "Player: " + p.getName() + "\n" + "ItemType: " + i.getType() + "\n"; - if (Sentinel.deop) { + if (Config.deop) { p.setOp(false); logMessage = logMessage + "Operator Removed: ✔\n"; opRemoved = true; @@ -93,7 +98,7 @@ public class DeniedActions { logMessage = logMessage + "Operator Removed: ✘\n"; opRemoved = false; } - if (Sentinel.ban) { + if (Config.ban) { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting to use an NBT item. Contact an administrator if you believe this to be a mistake."); logMessage = logMessage + "Banned: ✔\n"; banned = true; diff --git a/src/main/java/io/github/thetrouper/sentinel/server/util/ServerUtils.java b/src/main/java/io/github/thetrouper/sentinel/server/util/ServerUtils.java index e1a295b..71f3c2c 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/util/ServerUtils.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/util/ServerUtils.java @@ -1,23 +1,33 @@ -/** - * This file is for tutorial purposes made by ImproperIssues. Distribute if you want :) - */ - package io.github.thetrouper.sentinel.server.util; import io.github.thetrouper.sentinel.Sentinel; import io.github.thetrouper.sentinel.commands.InfoCommand; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; import org.bukkit.entity.Player; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Arrays; import java.util.List; -import java.util.Set; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Predicate; -/** - * Server utils - */ -public abstract class ServerUtils { +public class ServerUtils { + public static void sendCommand(String command) { + ServerUtils.sendDebugMessage("Getting scheduler"); + Bukkit.getScheduler().scheduleSyncDelayedTask(Sentinel.getInstance(), () -> { + try { + ServerUtils.sendDebugMessage("Attempting to run command..."); + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), command); + } catch (Exception e) { + e.printStackTrace(); + } + },1); + } public static void sendDebugMessage(String message) { if (InfoCommand.debugmode) { Sentinel.log.info(message); @@ -28,37 +38,57 @@ public abstract class ServerUtils { } } } - /** - * List of names of online players - * @return list of names - */ - public static List listPlayers() { - List list =new ArrayList<>(); - Bukkit.getOnlinePlayers().forEach(p -> list.add(p.getName())); - return list; + + public static List getPlayers() { + return new ArrayList<>(Bukkit.getOnlinePlayers()); } - /** - * List of names of online staff - * @return list of names - */ - public static List listStaff() { - List list =new ArrayList<>(); - Bukkit.getOnlinePlayers().forEach(p -> { - if (p.isOp()) list.add(p.getName()); - }); - return list; + public static List getStaff() { + return getPlayers().stream().filter(Player -> Player.hasPermission("sentinel.staff")).toList(); } - /** - * List of names of online staff - * @return list of staff - */ - public static Set getStaff() { - Set list = new HashSet<>(); - Bukkit.getOnlinePlayers().forEach(p -> { - if (p.isOp()) list.add(p); + public static void forEachPlayer(Consumer consumer) { + getPlayers().forEach(consumer); + } + + public static void forEachStaff(Consumer consumer) { + getStaff().forEach(consumer); + } + + public static void dmEachPlayer(Predicate condition, String dm) { + forEachPlayer(p -> { + if (condition.test(p)) p.sendMessage(dm); }); - return list; + } + + public static void dmEachPlayer(String dm) { + forEachPlayer(p -> p.sendMessage(dm)); + } + + public static void forEachSpecified(Iterable players, Consumer consumer) { + players.forEach(consumer); + } + + public static void forEachSpecified(Consumer consumer, Player... players) { + Arrays.stream(players).forEach(consumer); + } + public static void forEachPlayerRun(Predicate condition, Consumer task) { + forEachPlayer(p -> { + if (condition.test(p)) { + task.accept(p); + } + }); + } + public static void sendActionBar(Player p, String msg) { + p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(msg)); + } + + public static boolean hasBlockBelow(Player player, Material material) { + for (int y = player.getLocation().getBlockY() - 1; y >= player.getLocation().getBlockY() - 12; y--) { + if (player.getWorld().getBlockAt(player.getLocation().getBlockX(), y, player.getLocation().getBlockZ()).getType() == material) { + return true; + } + } + return false; } } diff --git a/src/main/java/io/github/thetrouper/sentinel/server/util/TextUtils.java b/src/main/java/io/github/thetrouper/sentinel/server/util/TextUtils.java index 87ebf73..56d1b6b 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/util/TextUtils.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/util/TextUtils.java @@ -2,11 +2,58 @@ package io.github.thetrouper.sentinel.server.util; import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; + +import java.util.Map; +import java.util.regex.PatternSyntaxException; public class TextUtils { + public static String color(String s) { + return s.replaceAll("&","§"); + } public static String prefix(String text) { String prefix = Sentinel.prefix; return prefix + text; } + public static String replaceRepeatingLetters(String message) { + StringBuilder result = new StringBuilder(); + char prevChar = '\0'; + int count = 0; + for (char c : message.toCharArray()) { + if (c == prevChar) { + count++; + if (count <= 3) { + result.append(c); + } + } else { + prevChar = c; + count = 1; + result.append(c); + } + } + + return result.toString(); + } + public static String fromLeetString(String s) { + Map dictionary = Config.leetPatterns; + String msg = s; + + for (String key : dictionary.keySet()) { + if (!s.contains(key)) continue; + try { + if (key.equals("$")) { + msg = msg.replaceAll("\\$", "s"); + } + else { + msg = msg.replaceAll(key, dictionary.get(key)); + } + } + catch (PatternSyntaxException ex) { + String regex = "[" + key + "]"; + msg = msg.replaceAll(regex, dictionary.get(key)); + } + } + return msg; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 57c3473..d1bc450 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -11,39 +11,73 @@ # Be sure to check out their amazing discord bot! config : plugin: - # ------------------------------- - # Important Setup (Do this first) - # ------------------------------- + # -------------------------------- + # Anti-Nuke Setup (Do this first) + # -------------------------------- prefix: "§d§lSentinel §8» §7" # Prefix of the plugin. Line below is the discord webhook for logs to be sent to webhook: "https://discord.com/api/webhooks/1124908469842096211/7NGOeFvtmxQ4n0_hSvbqhZUjnzRHIicLpHKETYU92n9JaLUPPsueBSn7w4wUfAnhjlLF" trusted: # List the UUIDs of players who are trusted, will bypass the plugin and be immune to logs and are able to re-op themeselves - "049460f7-21cb-42f5-8059-d42752bf406f" # obvWolf block-specific: true # Defaulted true | Weather or not to block ALL plugin specific commands from non-trusted members (EX: minecraft:ban) these will not be logged. prevent-nbt: true # Defaulted true | Should NBT items be blocked from the creative hotbar - log-nbt: true # Defaulted true | Should items and their NBT's be logged prevent-cmdblocks: true # Defaulted true | Should all command block actions be blocked - log-cmdblocks: true # Defaulted true | Log attempts of command-block place-ery dangerous: # These commands can only be run by "trusted" users - "op" - "deop" - "stop" - "execute" - "sudo" - - "" log-dangerous: true # Default true | Weather or not to log when a dangerous command is executed + log-cmdblocks: true # Defaulted true | Log attempts of command-block place-ery + log-nbt: true # Defaulted true | Should items and their NBT's be logged logged: # Commands that will always be logged when executed. - "gamemode" - "give" deop: true # Defaulted true | This will remove an untrusted player's operator permissions whenever they attempt dangerous actions - ban: false # Defaulted false | This will ban a player when they attempt dangerous actions + ban: false + nbt-punish: false # Defaulted false | This will ban a player when they attempt to use an NBT item + cmdblock-punish: false # Defaulted false | This will ban a player when they attempt to use a command block + command-punish: true # Defaulted true | This will ban a player when they attempt to use a dangerous command reop-command: false # Defaulted false | This enables the command allowing trusted players to op themselves if they get deoped. # ------------------------------- # Chat Filter Setup & AntiSpam # ------------------------------- chat: - # AntiSpam Heat system - anti-spam: true # Default true | Enables the anti-spam - default-gain: 1 # Heat gained as base for every message - medium-gain: 3 # Heat gained when your message is 50% similar - high-gain: 6 # Heat gained for - max-heat: 15 # Highest value of heat a player can reach \ No newline at end of file + anti-unicode: true # Default true | Prevents all non A-Z 0-9 + specials from being sent in chat + anti-spam: + # AntiSpam Heat system + enabled: true # Default true | Enables/disables the entire anti-spam + default-gain: 1 # Default 1 | Heat gained as base for every message + low-gain: 2 # Default 2 | Heat gained when message is >25% similar + medium-gain: 4 # Default 4 | Heat gained when message is >50% similar + high-gain: 6 # Default 6 | Heat gained for >90% similarity + heat-decay: 1 # Default 1 | Heat lost every second + block-heat: 10 # Default 10 | The heat required to block the message + punish-heat: 15 # Default 15 | The heat required to punish the player + punish-command: "mute %player% 1m Please refrain from spamming!" # (Use %player% for the player's name placeholder) + log-spam: true # Default true | logs spam punishments to the webhook + anti-swear: + enabled: true # Default true | Enables/disables the entire anti-swear + low-score: 0 # Default 0 | How much score should you gain for not attempting a bypass + medium-low-score: 0 # Default 0 | How much score should you gain for "bad "bypass attempt + medium-score: 1 # Default 1 | How much score should you gain for "ok" bypass attempt + medium-high-score: 3 # Default 3 | How much score should you gain for "good" bypass attempt + high-score: 5 # Default 5 | How much score should be gained for "extreme" attempt to bypass + punish-score: 20 # Default 20 | how much score is required to get punished + slur-insta-punish: true # Default true | Should players get insta punished for any words on the "slurs" list? + punish-command: "mute %player% 15m Please refrain from excessive use of profanity!" + slur-command: "mute %player% 1h Discriminatory speech is not tolerated on this server!" + log-swear: true # Default true | Logs swear punishments to the webhook + whitelisted: # List known false positives here + - glass + blacklisted: # list all the swears you wish to blacklist here + - ass + slurs: # List slurs here that you wish to insta-punish for attempting + - nigg + leet-patterns: + '0': o + '1': i + '3': e + '4': a + + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e573606..06cb79f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -9,6 +9,29 @@ permissions: sentinel.info: description: Permission to view sentinel info default: op + sentinel.staff: + description: Receive anti-swear and anti-spam warnings + default: op + sentinel.chat.antiswear.flags: + description: See antiSwear flags + default: op + sentinel.chat.antiswear.bypass: + description: Bypass the antiSwear + default: op + sentinel.chat.antispam.flags: + description: See antispam flags + default: op + sentinel.chat.antispam.bypass: + description: Bypass the antispam + default: op + sentinel.chat.*: + description: bypass all chat rules and see all flags + default: op + children: + sentinel.chat.antiswear.flags: true + sentinel.chat.antiswear.bypass: true + sentinel.chat.antispam.flags: true + sentinel.chat.antispam.bypass: true commands: sentinel: description: An info command.