Removed dependency on ProtocolLib, added silent mode to chat filters, improved loading times. Updated chat event handlers to use modern systems. Updated to 1.21.4. Abstracted chat actions, made URL and Unicode blockers standard chat filters, integrating profanity and slur regex into their respective filters. Made sentinel command not spam console when you make a mistake.
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
package me.trouper.sentinel;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import io.github.itzispyder.pdk.PDK;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import me.trouper.sentinel.data.WhitelistStorage;
|
||||
import me.trouper.sentinel.data.config.*;
|
||||
import me.trouper.sentinel.data.config.lang.LanguageFile;
|
||||
import me.trouper.sentinel.server.events.PluginCloakingPacket;
|
||||
import me.trouper.sentinel.startup.Auth;
|
||||
import me.trouper.sentinel.startup.Load;
|
||||
import me.trouper.sentinel.startup.IndirectLaunch;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@@ -17,9 +18,12 @@ import java.util.logging.Logger;
|
||||
|
||||
public final class Sentinel extends JavaPlugin {
|
||||
|
||||
private static final File dataFolder = new File("plugins/SentinelAntiNuke");
|
||||
public static final Logger log = Bukkit.getLogger();
|
||||
private static Sentinel instance;
|
||||
public static LanguageFile lang;
|
||||
public static File us;
|
||||
|
||||
private static final File dataFolder = new File("plugins/SentinelAntiNuke");
|
||||
private static final File violationcfg = new File(Sentinel .dataFolder(),"/violation-config.json");
|
||||
private static final File cfgfile = new File(Sentinel.dataFolder(),"/main-config.json");
|
||||
private static final File nbtcfg = new File(Sentinel.dataFolder(), "/nbt-config.json");
|
||||
@@ -37,51 +41,46 @@ public final class Sentinel extends JavaPlugin {
|
||||
public static StrictConfig strictConfig = JsonSerializable.load(strctcfg, StrictConfig.class, new StrictConfig());
|
||||
public static NBTConfig nbtConfig = JsonSerializable.load(nbtcfg, NBTConfig.class, new NBTConfig());
|
||||
public static AdvancedConfig advConfig = JsonSerializable.load(advcfg, AdvancedConfig.class, new AdvancedConfig());
|
||||
public static LanguageFile lang;
|
||||
public static File us;
|
||||
|
||||
public static ProtocolManager protocolManager;
|
||||
public static boolean doNoPlugins = false;
|
||||
public String identifier;
|
||||
public String license;
|
||||
public String nonce;
|
||||
public String ip;
|
||||
public int port;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
Sentinel.log.info("\n]======------ Pre-load started ------======[");
|
||||
|
||||
Sentinel.log.info("Setting PacketEvents API");
|
||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
|
||||
|
||||
Sentinel.log.info("Loading PacketEvents");
|
||||
PacketEvents.getAPI().load();
|
||||
|
||||
Sentinel.log.info("Registering PacketEvents");
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new PluginCloakingPacket());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
log.info("\n]======------ Pre-load started! ------======[");
|
||||
log.info("\n]======------ Loading Sentinel ------======[");
|
||||
|
||||
log.info("Initializing PacketEvents");
|
||||
|
||||
PacketEvents.getAPI().init();
|
||||
|
||||
log.info("Initializing PDK");
|
||||
PDK.init(this);
|
||||
|
||||
log.info("Instantiating plugin");
|
||||
instance = this;
|
||||
us = getFile();
|
||||
|
||||
log.info("Loading Config...");
|
||||
|
||||
loadConfig();
|
||||
|
||||
log.info("Loading ProtocolLib");
|
||||
|
||||
if (Bukkit.getServer().getPluginManager().isPluginEnabled("ProtocolLib") && mainConfig.plugin.pluginHider) {
|
||||
doNoPlugins = true;
|
||||
protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
} else {
|
||||
doNoPlugins = false;
|
||||
log.warning("Sentinel: ProtocolLib not found. Sentinel will attempt to hide your plugins through Bukkit's systems, although it may not catch everything.");
|
||||
}
|
||||
|
||||
log.info("Language Status: (%s)".formatted(lang.brokenLang));
|
||||
|
||||
log.info("Initializing Server ID...");
|
||||
|
||||
String serverID = Auth.getServerID();
|
||||
String license = Sentinel.mainConfig.plugin.license;
|
||||
String nonce = Auth.getNonce();
|
||||
int port = Auth.getPort();
|
||||
|
||||
log.info("Pre-load finished!\n]====---- Requesting Authentication ----====[ \n- License Key: %s\n- Server ID: %s\n- Nonce: %s\n".formatted(license,serverID,nonce));
|
||||
|
||||
Load.load(license,serverID);
|
||||
IndirectLaunch.launch();
|
||||
}
|
||||
|
||||
public static void loadConfig() {
|
||||
|
||||
public void loadConfig() {
|
||||
// Init
|
||||
mainConfig = JsonSerializable.load(cfgfile,MainConfig.class,new MainConfig());
|
||||
advConfig = JsonSerializable.load(advcfg,AdvancedConfig.class,new AdvancedConfig());
|
||||
@@ -91,7 +90,6 @@ public final class Sentinel extends JavaPlugin {
|
||||
nbtConfig = JsonSerializable.load(nbtcfg,NBTConfig.class,new NBTConfig());
|
||||
violationConfig = JsonSerializable.load(violationcfg,ViolationConfig.class,new ViolationConfig());
|
||||
|
||||
|
||||
// Save
|
||||
mainConfig.save();
|
||||
advConfig.save();
|
||||
@@ -108,11 +106,15 @@ public final class Sentinel extends JavaPlugin {
|
||||
|
||||
lang = JsonSerializable.load(LanguageFile.PATH,LanguageFile.class,new LanguageFile());
|
||||
lang.save();
|
||||
|
||||
log.info("Setting License Key");
|
||||
license = Auth.getLicenseKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Plugin shutdown logic
|
||||
PacketEvents.getAPI().terminate();
|
||||
log.info("Sentinel has disabled! (%s) Your server is now no longer protected!".formatted(getDescription().getVersion()));
|
||||
}
|
||||
|
||||
@@ -123,5 +125,4 @@ public final class Sentinel extends JavaPlugin {
|
||||
public static File dataFolder() {
|
||||
return dataFolder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,27 +19,27 @@ public class AdvancedConfig implements JsonSerializable<AdvancedConfig> {
|
||||
return file;
|
||||
}
|
||||
public List<String> fakePlugins = Arrays.asList(
|
||||
"nocheatplus",
|
||||
"negativity",
|
||||
"warden",
|
||||
"horizon",
|
||||
"illegalstack",
|
||||
"coreprotect",
|
||||
"exploitsx",
|
||||
"vulcan",
|
||||
"abc",
|
||||
"spartan",
|
||||
"kauri",
|
||||
"anticheatreloaded",
|
||||
"witherac",
|
||||
"godseye",
|
||||
"matrix",
|
||||
"wraith",
|
||||
"antixrayheuristics",
|
||||
"grimac"
|
||||
"Nocheatplus",
|
||||
"Negativity",
|
||||
"Warden",
|
||||
"Horizon",
|
||||
"Illegalstack",
|
||||
"CoreProtect",
|
||||
"ExploitsX",
|
||||
"Vulcan",
|
||||
"ABC",
|
||||
"Spartan",
|
||||
"Kauri",
|
||||
"AnticheatReloaded",
|
||||
"WitherAC",
|
||||
"GodsEye",
|
||||
"Matrix",
|
||||
"Wraith",
|
||||
"AntiXrayHeuristics",
|
||||
"GrimAC"
|
||||
);
|
||||
|
||||
public List<String> versionAliases = List.of(
|
||||
public List<String> commandsWithPluginAccess = Arrays.asList(
|
||||
"version",
|
||||
"bukkit:version",
|
||||
"ver",
|
||||
@@ -56,6 +56,19 @@ public class AdvancedConfig implements JsonSerializable<AdvancedConfig> {
|
||||
"bukkit:help"
|
||||
);
|
||||
|
||||
public List<String> pluginTabCompletions = Arrays.asList(
|
||||
"version",
|
||||
"bukkit:version",
|
||||
"ver",
|
||||
"bukkit:ver",
|
||||
"about",
|
||||
"bukkit:about",
|
||||
"?",
|
||||
"bukkit:?",
|
||||
"help",
|
||||
"bukkit:help"
|
||||
);
|
||||
|
||||
public Map<String, String> leetPatterns = new HashMap<>() {{
|
||||
put("0", "o");
|
||||
put("1", "i");
|
||||
@@ -75,9 +88,4 @@ public class AdvancedConfig implements JsonSerializable<AdvancedConfig> {
|
||||
put("<", "c");
|
||||
put("v", "u");
|
||||
}};
|
||||
public String allowedCharRegex = "[A-Za-z0-9\\[,./?><|\\]§()*&^%$#@!~`{}:;'\"-_]";
|
||||
public String falsePosRegex = "";
|
||||
public String swearRegex = "";
|
||||
public String strictRegex = "";
|
||||
public String urlRegex = "\\b(?:(?:https?|ftp):\\/\\/)?(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:com|org|net|int|edu|gov|mil|arpa|biz|info|mobi|app|name|aero|jobs|museum|travel|a[c-gil-oq-uwxz]|b[abd-jmnoq-tvwyz]|c[acdf-ik-orsu-z]|d[dejkmoz]|e[ceghr-u]|f[ijkmor]|g[abd-ilmnp-uwy]|h[kmnrtu]|i[delmnoq-t]|j[emop]|k[eghimnprwyz]|l[abcikr-vy]|m[acdeghk-z]|n[acefgilopruz]|om|p[ae-hk-nrstwy]|qa|r[eosuw]|s[a-eg-or-vxyz]|t[cdfghj-prtvwz]|u[agksyz]|v[aceginu]|w[fs]|y[etu]|z[amrw])))(?::\\d{2,5})?(?:\\/\\S*)?\\b";
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ public class FPConfig implements JsonSerializable<FPConfig> {
|
||||
return file;
|
||||
}
|
||||
|
||||
public List<String> swearWhitelist = new ArrayList<>(Arrays.asList(
|
||||
|
||||
public String regexWhitelist = "";
|
||||
public boolean useRegex = false;
|
||||
public List<String> swearWhitelist = Arrays.asList(
|
||||
"but then",
|
||||
"was scamming",
|
||||
"an alt",
|
||||
@@ -75,5 +78,5 @@ public class FPConfig implements JsonSerializable<FPConfig> {
|
||||
"but its",
|
||||
"whoever",
|
||||
" again"
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MainConfig implements JsonSerializable<MainConfig> {
|
||||
@@ -17,16 +18,20 @@ public class MainConfig implements JsonSerializable<MainConfig> {
|
||||
file.getParentFile().mkdirs();
|
||||
return file;
|
||||
}
|
||||
|
||||
public boolean debugMode = false;
|
||||
public boolean telemetry = true;
|
||||
|
||||
public Plugin plugin = new Plugin();
|
||||
public Chat chat = new Chat();
|
||||
public boolean debugMode = false;
|
||||
public BackdoorDetection backdoorDetection = new BackdoorDetection();
|
||||
|
||||
public class Plugin {
|
||||
public String license = "null";
|
||||
public String prefix = "§d§lSentinel §8» §7";
|
||||
public String webhook = "https://discord.com/api/webhooks/id/token";
|
||||
public String lang = "en-us.json";
|
||||
public List<String> trustedPlayers = List.of(
|
||||
public List<String> trustedPlayers = Arrays.asList(
|
||||
"049460f7-21cb-42f5-8059-d42752bf406f"
|
||||
);
|
||||
|
||||
@@ -35,17 +40,21 @@ public class MainConfig implements JsonSerializable<MainConfig> {
|
||||
public String identifier = "My Server (Edit in main-config.json)";
|
||||
}
|
||||
|
||||
public class BackdoorDetection {
|
||||
public boolean enabled = false;
|
||||
public boolean setupMode = true;
|
||||
public boolean keepSetupMode = true;
|
||||
}
|
||||
|
||||
public class Chat {
|
||||
public AntiSwear swearFilter = new AntiSwear();
|
||||
public AntiSpam spamFilter = new AntiSpam();
|
||||
public boolean useAntiURL = true;
|
||||
public boolean useSwearRegex = false;
|
||||
public boolean useStrictRegex = false;
|
||||
public boolean useAntiUnicode = true;
|
||||
public ProfanityFilter profanityFilter = new ProfanityFilter();
|
||||
public SpamFilter spamFilter = new SpamFilter();
|
||||
public UnicodeFilter unicodeFilter = new UnicodeFilter();
|
||||
public UrlFilter urlFilter = new UrlFilter();
|
||||
|
||||
|
||||
public class AntiSpam {
|
||||
public class SpamFilter {
|
||||
public boolean enabled = true;
|
||||
public boolean silent = false;
|
||||
public int defaultGain = 1;
|
||||
public int lowGain = 2;
|
||||
public int mediumGain = 4;
|
||||
@@ -54,14 +63,19 @@ public class MainConfig implements JsonSerializable<MainConfig> {
|
||||
public int blockSimilarity = 99;
|
||||
public int blockHeat = 10;
|
||||
public int punishHeat = 25;
|
||||
public List<String> punishCommands = List.of(
|
||||
public List<String> whitelist = Arrays.asList(
|
||||
"welcome",
|
||||
"wl"
|
||||
);
|
||||
public List<String> punishCommands = Arrays.asList(
|
||||
"clearchat",
|
||||
"mute %player% 1m Please refrain from spamming!"
|
||||
);
|
||||
}
|
||||
|
||||
public class AntiSwear {
|
||||
public class ProfanityFilter {
|
||||
public boolean enabled = true;
|
||||
public boolean silent = false;
|
||||
public int lowScore = 0;
|
||||
public int mediumLowScore = 1;
|
||||
public int mediumScore = 3;
|
||||
@@ -70,13 +84,39 @@ public class MainConfig implements JsonSerializable<MainConfig> {
|
||||
public int regexScore = 4;
|
||||
public int scoreDecay = 3;
|
||||
public int punishScore = 20;
|
||||
public List<String> swearPunishCommands = List.of(
|
||||
public List<String> profanityPunishCommands = Arrays.asList(
|
||||
"mute %player% 15m Do not attempt to bypass the Profanity Filter"
|
||||
);
|
||||
public List<String> strictPunishCommands = List.of(
|
||||
public List<String> strictPunishCommands = Arrays.asList(
|
||||
"mute %player% 1h Discriminatory speech is not tolerated on this server!"
|
||||
);
|
||||
}
|
||||
|
||||
public class UnicodeFilter {
|
||||
public boolean enabled = true;
|
||||
public boolean silent = false;
|
||||
public boolean punished = false;
|
||||
public String regex = "[^A-Za-z0-9\\[,./?><|\\]§()*&^%$#@!~`{}:;'\"-_]";
|
||||
public List<String> punishCommands = Arrays.asList(
|
||||
"clearchat",
|
||||
"mute %player% 1m Please refrain from spamming!"
|
||||
);
|
||||
}
|
||||
|
||||
public class UrlFilter {
|
||||
public boolean enabled = true;
|
||||
public boolean silent = false;
|
||||
public boolean punished = true;
|
||||
public String regex = "\\b(?:(?:https?|ftp):\\/\\/)?(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:com|org|net|int|edu|gov|mil|arpa|biz|info|mobi|app|name|aero|jobs|museum|travel|a[c-gil-oq-uwxz]|b[abd-jmnoq-tvwyz]|c[acdf-ik-orsu-z]|d[dejkmoz]|e[ceghr-u]|f[ijkmor]|g[abd-ilmnp-uwy]|h[kmnrtu]|i[delmnoq-t]|j[emop]|k[eghimnprwyz]|l[abcikr-vy]|m[acdeghk-z]|n[acefgilopruz]|om|p[ae-hk-nrstwy]|qa|r[eosuw]|s[a-eg-or-vxyz]|t[cdfghj-prtvwz]|u[agksyz]|v[aceginu]|w[fs]|y[etu]|z[amrw])))(?::\\d{2,5})?(?:\\/\\S*)?\\b";
|
||||
public List<String> whitelist = Arrays.asList(
|
||||
"play.example.com/this-could-be-your-server-ip",
|
||||
"store.example.com/for-sharing-your-store",
|
||||
"example.com/these-can-even-be-regex"
|
||||
);
|
||||
public List<String> punishCommands = Arrays.asList(
|
||||
"clearchat",
|
||||
"mute %player% 1m Please refrain from spamming!"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||
public int globalMaxEnchant = 5;
|
||||
public int maxMending = 1;
|
||||
public int maxUnbreaking = 3;
|
||||
public int maxVanishing = 1;
|
||||
public int maxCurseOfVanishing = 1;
|
||||
public int maxAquaAffinity = 1;
|
||||
public int maxBlastProtection = 4;
|
||||
public int maxCurseOfBinding = 1;
|
||||
@@ -57,4 +57,7 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||
public int maxLuckOfTheSea = 3;
|
||||
public int maxLure = 3;
|
||||
public int maxSilkTouch = 1;
|
||||
public int maxBreach = 4;
|
||||
public int maxDensity = 5;
|
||||
public int maxWindBurst = 3;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import me.trouper.sentinel.Sentinel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class StrictConfig implements JsonSerializable<StrictConfig> {
|
||||
@@ -15,14 +16,15 @@ public class StrictConfig implements JsonSerializable<StrictConfig> {
|
||||
return file;
|
||||
}
|
||||
|
||||
public List<String> strict = new ArrayList<>() {{
|
||||
add("nigg");
|
||||
add("niger");
|
||||
add("nlgg");
|
||||
add("nlger");
|
||||
add("njgg");
|
||||
add("tranny");
|
||||
add("fag");
|
||||
add("beaner");
|
||||
}};
|
||||
public String regexStrict = "";
|
||||
public boolean useRegex = false;
|
||||
public List<String> strict = Arrays.asList(
|
||||
"nigg",
|
||||
"niger",
|
||||
"nlg",
|
||||
"tranny",
|
||||
"fag",
|
||||
"beaner",
|
||||
"retard"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SwearsConfig implements JsonSerializable<SwearsConfig> {
|
||||
@@ -14,7 +15,9 @@ public class SwearsConfig implements JsonSerializable<SwearsConfig> {
|
||||
return file;
|
||||
}
|
||||
|
||||
public List<String> swears = List.of(
|
||||
public String regexSwears = "";
|
||||
public boolean useRegex = false;
|
||||
public List<String> swears = Arrays.asList(
|
||||
"anal",
|
||||
"anus",
|
||||
"arse",
|
||||
@@ -76,7 +79,6 @@ public class SwearsConfig implements JsonSerializable<SwearsConfig> {
|
||||
"queer",
|
||||
"rape",
|
||||
"rapist",
|
||||
"retard",
|
||||
"rimjob",
|
||||
"scrotum",
|
||||
"sex",
|
||||
|
||||
@@ -2,10 +2,9 @@ package me.trouper.sentinel.data.config;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.startup.Load;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
@@ -31,7 +30,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -41,7 +40,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -51,7 +50,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -61,7 +60,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -71,7 +70,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -81,7 +80,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
@@ -92,7 +91,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public Specific specific = new Specific();
|
||||
public class Dangerous {
|
||||
public boolean enabled = true;
|
||||
public List<String> commands = List.of(
|
||||
public List<String> commands = Arrays.asList(
|
||||
"op",
|
||||
"deop",
|
||||
"stop",
|
||||
@@ -119,13 +118,13 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean deop = true;
|
||||
public boolean logToDiscord = true;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
public class Logged {
|
||||
public boolean enabled = true;
|
||||
public List<String> commands = List.of(
|
||||
public List<String> commands = Arrays.asList(
|
||||
"give",
|
||||
"item"
|
||||
);
|
||||
@@ -135,7 +134,7 @@ public class ViolationConfig implements JsonSerializable<SwearsConfig> {
|
||||
public boolean enabled = true;
|
||||
public boolean logToDiscord = false;
|
||||
public boolean punish = false;
|
||||
public List<String> punishmentCommands = List.of(
|
||||
public List<String> punishmentCommands = Arrays.asList(
|
||||
"ban %player% ]=- Sentinel -=[ \nYou have been banned for attempting a dangerous action. \nIf you believe this to be a mistake, please contact the server owner."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,9 +66,13 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
public Chat chat = new Chat();
|
||||
public class Chat {
|
||||
public Profanity profanity = new Profanity();
|
||||
public Spam spam = new Spam();
|
||||
public Unicode unicode = new Unicode();
|
||||
public URL url = new URL();
|
||||
|
||||
public class Profanity {
|
||||
public String prevent = "has been prevented from swearing.";
|
||||
public String autoPunish = "has been auto-punished for swearing.";
|
||||
public String preventNotification = "has been prevented from swearing.";
|
||||
public String autoPunishNotification = "has been auto-punished for swearing.";
|
||||
public String preventWarning = "Do not use profanity in chat. Any attempt to bypass this filter will be detected, and you will be punished.";
|
||||
public String autoPunishWarning = "&cYou have been auto-punished for attempting to bypass the profanity filter!";
|
||||
|
||||
@@ -77,7 +81,7 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
public String uuid = "UUID";
|
||||
public String score = "Score";
|
||||
|
||||
public String reportInfoTitle = "Anti-Swear Detection";
|
||||
public String reportInfoTitle = "Profanity Filter Detection";
|
||||
public String originalMessage = "Original Message";
|
||||
public String processedMessage = "Processed Message";
|
||||
public String severity = "Severity";
|
||||
@@ -87,41 +91,9 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
public String commandAction = "Executed Punishment Commands";
|
||||
}
|
||||
|
||||
public Regex regex = new Regex();
|
||||
public class Regex {
|
||||
public String autoPunish = "has been auto-punished for slurs. (Regex)";
|
||||
public String regexTrigger = "has triggered a regex blocker.";
|
||||
|
||||
public String urlBlockName = "URL Regex Blocker";
|
||||
public String urlBlockMessage = "Your message has been withheld. Do not link to websites.";
|
||||
|
||||
public String unicodeBlockName = "Unicode Regex Blocker";
|
||||
public String unicodeBlockMessage = "Your message has been withheld. Please only use characters found on your keyboard.";
|
||||
|
||||
public String swearBlockName = "Swear Regex Blocker";
|
||||
public String swearBlockMessage = "Your message has been withheld for containing blocked words.";
|
||||
|
||||
public String strictBlockName = "Strict Regex Blocker";
|
||||
public String strictBlockMessage = "Your message has been withheld for containing slurs.";
|
||||
|
||||
public String treeTitle = "A Regex Filter has been triggered.";
|
||||
public String playerInfoTitle = "Player: %s";
|
||||
public String uuid = "UUID";
|
||||
public String score = "Score";
|
||||
|
||||
public String reportInfoTitle = "%s Detection";
|
||||
public String originalMessage = "Original Message";
|
||||
public String flaggedMessage = "Flagged Message";
|
||||
|
||||
public String actionTitle = "Actions";
|
||||
public String blockAction = "Blocked the message";
|
||||
public String commandAction = "Executed Punishment Commands";
|
||||
}
|
||||
|
||||
public Spam spam = new Spam();
|
||||
public class Spam {
|
||||
public String autoPunish = "has been auto-punished for spamming.";
|
||||
public String spamWarning = "might be spamming!";
|
||||
public String autoPunishNotification = "has been auto-punished for spamming.";
|
||||
public String preventNotification = "might be spamming!";
|
||||
public String preventWarning = "Do not spam in chat! Please wait before sending another message.";
|
||||
public String autoPunishWarning = "&cYou have been auto-punished for violating the anti-spam repetitively!";
|
||||
|
||||
@@ -130,7 +102,7 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
public String uuid = "UUID";
|
||||
public String heat = "Heat";
|
||||
|
||||
public String reportInfoTitle = "Anti-Spam Detection";
|
||||
public String reportInfoTitle = "Spam Filter Detection";
|
||||
public String previousMessage = "Previous Message";
|
||||
public String currentMessage = "Current Message";
|
||||
public String similarity = "Similarity";
|
||||
@@ -139,6 +111,44 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
public String blockAction = "Blocked the message";
|
||||
public String commandAction = "Executed Punishment Commands";
|
||||
}
|
||||
|
||||
public class Unicode {
|
||||
public String autoPunishNotification = "has been punished for triggering the Unicode filter.";
|
||||
public String preventNotification = "has been prevented from using invalid Unicode characters.";
|
||||
public String autoPunishWarning = "You have been punished for triggered the Unicode filter.";
|
||||
public String preventWarning = "You may only use unicode from the QWERTY keyboard.";
|
||||
|
||||
public String treeTitle = "The Unicode Filter has been triggered.";
|
||||
public String playerInfoTitle = "Player: %s";
|
||||
public String uuid = "UUID";
|
||||
|
||||
public String reportInfoTitle = "Unicode Filter Detection";
|
||||
public String originalMessage = "Original Message";
|
||||
public String highlightedMessage = "Highlighted Message";
|
||||
|
||||
public String actionTitle = "Actions";
|
||||
public String blockAction = "Blocked the message";
|
||||
public String commandAction = "Executed Punishment Commands";
|
||||
}
|
||||
|
||||
public class URL {
|
||||
public String autoPunishNotification = "has been punished for triggering the URL filter.";
|
||||
public String preventNotification = "has been prevented from sending a URL.";
|
||||
public String autoPunishWarning = "You have been punished for triggered the URL filter.";
|
||||
public String preventWarning = "You may not send links in chat.";
|
||||
|
||||
public String treeTitle = "The URL Filter has been triggered.";
|
||||
public String playerInfoTitle = "Player: %s";
|
||||
public String uuid = "UUID";
|
||||
|
||||
public String reportInfoTitle = "URL Filter Detection";
|
||||
public String originalMessage = "Original Message";
|
||||
public String highlightedMessage = "Highlighted Message";
|
||||
|
||||
public String actionTitle = "Actions";
|
||||
public String blockAction = "Blocked the message";
|
||||
public String commandAction = "Executed Punishment Commands";
|
||||
}
|
||||
}
|
||||
public CommandBlockEdit commandBlockEdit = new CommandBlockEdit();
|
||||
public class CommandBlockEdit {
|
||||
@@ -224,9 +234,7 @@ public class LanguageFile implements JsonSerializable<LanguageFile> {
|
||||
|
||||
public CommandExecute commandExecute = new CommandExecute();
|
||||
public class CommandExecute {
|
||||
public String specificCommandDetection = "A player has attempted to run a specific command.";
|
||||
public String dangerousCommandDetection = "A player has attempted to run a dangerous command.";
|
||||
public String loggedCommandDetection = "A player has ran a logged command.";
|
||||
public String specificCommandDetection = "A player has attempted to run a %s command.";
|
||||
public String playerInfoTitle = "Player: %s";
|
||||
public String uuid = "UUID";
|
||||
public String location = "Location";
|
||||
|
||||
@@ -5,30 +5,32 @@ import io.github.itzispyder.pdk.commands.CommandRegistry;
|
||||
import io.github.itzispyder.pdk.commands.CustomCommand;
|
||||
import io.github.itzispyder.pdk.commands.Permission;
|
||||
import io.github.itzispyder.pdk.commands.completions.CompletionBuilder;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.regex.AntiRegex;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.AntiProfanity;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.AntiSpam;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.data.types.WhitelistedBlock;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.unicode.UnicodeFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.url.UrlFilter;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.startup.Auth;
|
||||
import me.trouper.sentinel.startup.Load;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.chat.SignedMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.CommandBlock;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -54,62 +56,19 @@ public class SentinelCommand implements CustomCommand {
|
||||
&8- &7(Its only 5$)
|
||||
&fIf you are reading this from a decompiler:
|
||||
&8- &7Please stop trying to crack the plugin and purchase it!
|
||||
&8- &7Your time spent trying trying to bypass my DRM could be spent at a minimum wage job.
|
||||
&8- &7Your time spent trying to bypass my DRM could be spent at a minimum wage job.
|
||||
&8- &7There you will make 7$ an hour! (As oppose to 5$ for multiple hours of cracking)
|
||||
&fWoah! You read quite far!
|
||||
&8- &7Want the plugin for cheaper, &nor even for free&r&7?
|
||||
&8- &7DM &b@obvwolf&7 on discord and lets make a deal!
|
||||
""".formatted(Auth.getLicenseKey(),Auth.getServerID(), MainConfig.username));
|
||||
""".formatted(Sentinel.getInstance().license,Sentinel.getInstance().identifier, MainConfig.username));
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(CommandSender sender, Command command, String s, Args args) {
|
||||
if (Load.lite) {
|
||||
if (!args.isEmpty() && args.get(0).toString().equals("reload")) {
|
||||
if (!(sender instanceof ConsoleCommandSender) && !PlayerUtils.isTrusted((Player) sender)) {
|
||||
sender.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust));
|
||||
return;
|
||||
}
|
||||
Sentinel.log.info("Sentinel is now Reloading the config.");
|
||||
sender.sendMessage(Text.prefix("Reloading the config."));
|
||||
Sentinel.loadConfig();
|
||||
String serverID = Auth.getServerID();
|
||||
String license = Sentinel.mainConfig.plugin.license;
|
||||
String nonce = Auth.getNonce();
|
||||
Auth.getPort();
|
||||
|
||||
Sentinel.log.info("\n]====---- Requesting Authentication ----====[ \n- License Key: %s\n- Server ID: %s\n- Nonce: %s\n".formatted(license, serverID, nonce));
|
||||
if (Auth.canLoad()) {
|
||||
Load.startup(false);
|
||||
return;
|
||||
}
|
||||
Sentinel.log.info("Re-authentication Failed.");
|
||||
} else {
|
||||
sender.sendMessage(liteMode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (args.getSize() > 0 == args.get(0).toString().equals("reload")) {
|
||||
if (sender instanceof Player p) {
|
||||
if (!PlayerUtils.isTrusted(p)) {
|
||||
p.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust));
|
||||
return;
|
||||
}
|
||||
}
|
||||
Sentinel.log.info("Sentinel is now Reloading the config.");
|
||||
sender.sendMessage(Text.prefix("Reloading the config."));
|
||||
Sentinel.loadConfig();
|
||||
}
|
||||
Player p = (Player) sender;
|
||||
if (!p.hasPermission("sentinel.staff")) return;
|
||||
switch (args.get(0).toString()) {
|
||||
case "config" -> {
|
||||
if (!PlayerUtils.isTrusted(p)) return;
|
||||
if (!MainGUI.verify(p)) return;
|
||||
p.openInventory(new MainGUI().home.getInventory());
|
||||
}
|
||||
case "commandblock", "cb" -> handleCommandBlock(p,args);
|
||||
case "debug" -> handleDebugCommand(p,args);
|
||||
case "false-positive" -> handleFalsePositive(p,args);
|
||||
try {
|
||||
safety(sender,command,s,args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sender.sendMessage(Text.prefix("Invalid arguments, please check usage."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +86,66 @@ public class SentinelCommand implements CustomCommand {
|
||||
.then(b.arg("<player>","all"))));
|
||||
}
|
||||
|
||||
|
||||
public void safety(CommandSender sender, Command command, String s, Args args) {
|
||||
if (Load.lite) {
|
||||
handleLiteMessage(sender,args);
|
||||
return;
|
||||
}
|
||||
if (sender instanceof Player p && !p.hasPermission("sentinel.staff")) return;
|
||||
switch (args.get(0).toString()) {
|
||||
case "reload" -> {
|
||||
handleReload(sender);
|
||||
}
|
||||
case "config" -> {
|
||||
if (!(sender instanceof Player p) || !PlayerUtils.isTrusted(p)) return;
|
||||
if (!MainGUI.verify(p)) return;
|
||||
p.openInventory(new MainGUI().home.getInventory());
|
||||
}
|
||||
case "commandblock", "cb" -> {
|
||||
if (!(sender instanceof Player p) || !PlayerUtils.isTrusted(p)) return;
|
||||
handleCommandBlock(p,args);
|
||||
}
|
||||
case "debug" -> {
|
||||
if (!(sender instanceof Player p) || !PlayerUtils.isTrusted(p)) return;
|
||||
handleDebugCommand(p,args);
|
||||
}
|
||||
case "false-positive" -> {
|
||||
if (!(sender instanceof Player p)) return;
|
||||
handleFalsePositive(p,args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReload(CommandSender sender) {
|
||||
if (sender instanceof Player p && !PlayerUtils.isTrusted(p)) {
|
||||
p.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust));
|
||||
return;
|
||||
}
|
||||
Sentinel.log.info("Sentinel is now Reloading the config.");
|
||||
sender.sendMessage(Text.prefix("Reloading the config."));
|
||||
Sentinel.getInstance().loadConfig();
|
||||
}
|
||||
|
||||
private void handleLiteMessage(CommandSender sender, Args args) {
|
||||
if (!args.isEmpty() && args.get(0).toString().equals("reload")) {
|
||||
if (sender instanceof Player && !PlayerUtils.isTrusted((Player) sender)) {
|
||||
sender.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust));
|
||||
return;
|
||||
}
|
||||
Sentinel.log.info("Sentinel is now Reloading the config.");
|
||||
sender.sendMessage(Text.prefix("Reloading the config."));
|
||||
Sentinel.getInstance().loadConfig();
|
||||
|
||||
if (Load.load(Sentinel.getInstance().license, Sentinel.getInstance().identifier,false)) {
|
||||
return;
|
||||
}
|
||||
Sentinel.log.info("Re-authentication Failed.");
|
||||
} else {
|
||||
sender.sendMessage(liteMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFalsePositive(Player p, Args args) {
|
||||
if (!p.hasPermission("sentinel.chat.antiswear.edit")) {
|
||||
p.sendMessage(Sentinel.lang.permissions.noPermission);
|
||||
@@ -156,60 +175,64 @@ public class SentinelCommand implements CustomCommand {
|
||||
EmbedFormatter.sendEmbed(EmbedFormatter.format(root));
|
||||
}
|
||||
|
||||
private void handleCommandBlock(Player p, Args args) {
|
||||
if (!PlayerUtils.isTrusted(p)) {
|
||||
private void handleCommandBlock(CommandSender sender, Args args) {
|
||||
if ((sender instanceof Player p) && !PlayerUtils.isTrusted(p)) {
|
||||
p.sendMessage(Text.prefix(Sentinel.lang.permissions.noTrust));
|
||||
return;
|
||||
}
|
||||
Block target = p.getTargetBlock(Set.of(Material.AIR),10);
|
||||
switch (args.get(1).toString()) {
|
||||
case "add" -> {
|
||||
if (!(sender instanceof Player p)) return;
|
||||
Block target = p.getTargetBlock(Set.of(Material.AIR),10);
|
||||
if (target.getType().equals(Material.COMMAND_BLOCK) || target.getType().equals(Material.REPEATING_COMMAND_BLOCK) || target.getType().equals(Material.CHAIN_COMMAND_BLOCK)) {
|
||||
CommandBlock cb = (CommandBlock) target.getState();
|
||||
CBWhitelistManager.add(cb,p.getUniqueId());
|
||||
return;
|
||||
}
|
||||
p.sendMessage(Text.prefix("Could not whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it is not a command block!"));
|
||||
sender.sendMessage(Text.prefix("Could not whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it is not a command block!"));
|
||||
}
|
||||
case "remove" -> {
|
||||
if (!(sender instanceof Player p)) return;
|
||||
Block target = p.getTargetBlock(Set.of(Material.AIR),10);
|
||||
WhitelistedBlock wb = CBWhitelistManager.get(target.getLocation());
|
||||
if (wb != null) {
|
||||
CBWhitelistManager.remove(target.getLocation());
|
||||
p.sendMessage(Text.prefix("Successfully removed 1 &b" + Text.cleanName(WhitelistedBlock.fromSerialized(wb.loc()).getBlock().getType().toString()) + "&7 with the command &a" + wb.command() + "&7."));
|
||||
sender.sendMessage(Text.prefix("Successfully removed 1 &b" + Text.cleanName(WhitelistedBlock.fromSerialized(wb.loc()).getBlock().getType().toString()) + "&7 with the command &a" + wb.command() + "&7."));
|
||||
return;
|
||||
}
|
||||
p.sendMessage(Text.prefix("Could not un-whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it wasn't whitelisted in the first place!"));
|
||||
sender.sendMessage(Text.prefix("Could not un-whitelist the &b" + Text.cleanName(target.getType().toString()) + "&7 it wasn't whitelisted in the first place!"));
|
||||
}
|
||||
case "auto" -> {
|
||||
if (!(sender instanceof Player p)) return;
|
||||
if (CBWhitelistManager.autoWhitelist.contains(p.getUniqueId())) {
|
||||
CBWhitelistManager.autoWhitelist.remove(p.getUniqueId());
|
||||
p.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 off for you."));
|
||||
sender.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 off for you."));
|
||||
} else {
|
||||
CBWhitelistManager.autoWhitelist.add(p.getUniqueId());
|
||||
p.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 on for you."));
|
||||
sender.sendMessage(Text.prefix("Successfully toggled &bauto whitelist&7 on for you."));
|
||||
}
|
||||
}
|
||||
case "restore" -> {
|
||||
if (args.get(2).toString().equals("all")) {
|
||||
int result = CBWhitelistManager.restoreAll();
|
||||
p.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks.".formatted(result)));
|
||||
sender.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks.".formatted(result)));
|
||||
return;
|
||||
}
|
||||
String who = args.get(2).toString();
|
||||
UUID id = Bukkit.getOfflinePlayer(who).getUniqueId();
|
||||
int result = CBWhitelistManager.restoreAll(id);
|
||||
p.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks from &e%s&7.".formatted(result,who)));
|
||||
sender.sendMessage(Text.prefix("Successfully restored &b%s&7 command blocks from &e%s&7.".formatted(result,who)));
|
||||
}
|
||||
case "clear" -> {
|
||||
if (args.get(2).toString().equals("all")) {
|
||||
int result = CBWhitelistManager.clearAll();
|
||||
p.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks.".formatted(result)));
|
||||
sender.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks.".formatted(result)));
|
||||
return;
|
||||
}
|
||||
String who = args.get(2).toString();
|
||||
UUID id = Bukkit.getOfflinePlayer(who).getUniqueId();
|
||||
int result = CBWhitelistManager.clearAll(id);
|
||||
p.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks from &e%s&7.".formatted(result,who)));
|
||||
sender.sendMessage(Text.prefix("Successfully cleared &b%s&7 command blocks from &e%s&7.".formatted(result,who)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,10 +252,20 @@ public class SentinelCommand implements CustomCommand {
|
||||
Sentinel.mainConfig.save();
|
||||
}
|
||||
case "chat" -> {
|
||||
AsyncPlayerChatEvent message = new AsyncPlayerChatEvent(true,p,args.getAll(2).toString(), Set.of(p));
|
||||
AntiRegex.handleRegex(message);
|
||||
AntiSpam.handleAntiSpam(message);
|
||||
AntiProfanity.handleProfanityFilter(message);
|
||||
//true,p,args.getAll(2).toString(), Set.of(p)
|
||||
AsyncChatEvent message = new AsyncChatEvent(true,
|
||||
p,
|
||||
Set.of(p),
|
||||
ChatRenderer.defaultRenderer(),
|
||||
Component.text(args.getAll(2).toString()),
|
||||
Component.text(args.getAll(2).toString()),
|
||||
SignedMessage.system(args.getAll(2).toString(),
|
||||
Component.text(args.getAll(2).toString()))
|
||||
);
|
||||
UnicodeFilter.handleUnicodeFilter(message);
|
||||
UrlFilter.handleUrlFilter(message);
|
||||
SpamFilter.handleSpamFilter(message);
|
||||
ProfanityFilter.handleProfanityFilter(message);
|
||||
if (!message.isCancelled()) p.sendMessage(Text.prefix("Message did not get flagged."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ import net.kyori.adventure.text.event.ClickEvent;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@CommandRegistry(value = "sentineltab")
|
||||
public class TrapCommand implements CustomCommand {
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package me.trouper.sentinel.server.events;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
@@ -19,11 +19,11 @@ public class CBEditEvent implements CustomListener {
|
||||
|
||||
@EventHandler
|
||||
private void onCMDBlockChange(EntityChangeBlockEvent e) {
|
||||
ServerUtils.verbose("CommandBlockChange: Detected the event");
|
||||
//ServerUtils.verbose("CommandBlockChange: Detected the event");
|
||||
if (!Sentinel.violationConfig.commandBlockEdit.enabled) return;
|
||||
ServerUtils.verbose("CommandBlockChange: Enabled");
|
||||
//ServerUtils.verbose("CommandBlockChange: Enabled");
|
||||
if (!(e.getEntity() instanceof Player p)) return;
|
||||
ServerUtils.verbose("CommandBlockChange: Changer is a player");
|
||||
//ServerUtils.verbose("CommandBlockChange: Changer is a player");
|
||||
Block b = e.getBlock();
|
||||
if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK))
|
||||
return;
|
||||
|
||||
@@ -3,14 +3,14 @@ package me.trouper.sentinel.server.events;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
@@ -2,10 +2,10 @@ package me.trouper.sentinel.server.events;
|
||||
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -18,9 +18,9 @@ public class CBMCPlaceEvent implements CustomListener {
|
||||
private void onCMDMinecartPlace(PlayerInteractEvent e) {
|
||||
//ServerUtils.verbose("MinecartCommandPlace: Detected interaction");
|
||||
if (!Sentinel.violationConfig.commandBlockMinecartPlace.enabled) return;
|
||||
ServerUtils.verbose("MinecartCommandPlace: Check is enabled");
|
||||
//ServerUtils.verbose("MinecartCommandPlace: Check is enabled");
|
||||
if (!e.getPlayer().isOp()) return;
|
||||
ServerUtils.verbose("MinecartCommandPlace: Player is op");
|
||||
//ServerUtils.verbose("MinecartCommandPlace: Player is op");
|
||||
if (e.getItem() == null) return;
|
||||
ServerUtils.verbose("MinecartCommandPlace: Item isn't null");
|
||||
if (e.getClickedBlock() == null) return;
|
||||
|
||||
@@ -2,10 +2,10 @@ package me.trouper.sentinel.server.events;
|
||||
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -18,7 +18,7 @@ public class CBMCUseEvent implements CustomListener {
|
||||
private void onCMDBlockMinecartUse(PlayerInteractEntityEvent e) {
|
||||
//ServerUtils.verbose("MinecartCommandUse: Detected Interaction with entity");
|
||||
if (!Sentinel.violationConfig.commandBlockMinecartUse.enabled) return;
|
||||
ServerUtils.verbose("MinecartCommandUse: Enabled");
|
||||
//ServerUtils.verbose("MinecartCommandUse: Enabled");
|
||||
if (!e.getPlayer().isOp()) return;
|
||||
ServerUtils.verbose("MinecartCommandUse: Player op");
|
||||
if (e.getRightClicked().getType() != EntityType.COMMAND_BLOCK_MINECART) return;
|
||||
|
||||
@@ -3,11 +3,11 @@ package me.trouper.sentinel.server.events;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.CommandBlock;
|
||||
@@ -21,9 +21,9 @@ public class CBPlaceEvent implements CustomListener {
|
||||
private void onCMDBlockPlace(BlockPlaceEvent e) {
|
||||
//ServerUtils.verbose("CommandBlockPlace: Detected block place");
|
||||
if (!Sentinel.violationConfig.commandBlockPlace.enabled) return;
|
||||
ServerUtils.verbose("CommandBlockPlace: Enabled");
|
||||
//ServerUtils.verbose("CommandBlockPlace: Enabled");
|
||||
if (!e.getPlayer().isOp()) return;
|
||||
ServerUtils.verbose("CommandBlockPlace: Player is operator");
|
||||
//ServerUtils.verbose("CommandBlockPlace: Player is operator");
|
||||
Block b = e.getBlockPlaced();
|
||||
if (!(b.getType().equals(Material.COMMAND_BLOCK) ||
|
||||
b.getType().equals(Material.REPEATING_COMMAND_BLOCK) ||
|
||||
|
||||
@@ -3,11 +3,11 @@ package me.trouper.sentinel.server.events;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.CBWhitelistManager;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.CommandBlock;
|
||||
@@ -21,11 +21,11 @@ public class CBUseEvent implements CustomListener {
|
||||
private void onCMDBlockUse(PlayerInteractEvent e) {
|
||||
//ServerUtils.verbose("CommandBlockUse: Detected Interaction");
|
||||
if (!Sentinel.violationConfig.commandBlockUse.enabled) return;
|
||||
ServerUtils.verbose("CommandBlockUse: Enabled");
|
||||
//ServerUtils.verbose("CommandBlockUse: Enabled");
|
||||
if (!e.getPlayer().isOp()) return;
|
||||
ServerUtils.verbose("CommandBlockUse: Player is op");
|
||||
//ServerUtils.verbose("CommandBlockUse: Player is op");
|
||||
if (e.getClickedBlock() == null) return;
|
||||
ServerUtils.verbose("CommandBlockUse: Block isn't null");
|
||||
//ServerUtils.verbose("CommandBlockUse: Block isn't null");
|
||||
Block b = e.getClickedBlock();
|
||||
if (!(b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK)) return;
|
||||
CommandBlock cb = (CommandBlock) b.getState();
|
||||
|
||||
@@ -2,14 +2,17 @@ package me.trouper.sentinel.server.events;
|
||||
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import io.github.itzispyder.pdk.utils.SchedulerUtils;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.regex.AntiRegex;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.AntiProfanity;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.AntiSpam;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.unicode.UnicodeFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.url.UrlFilter;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.ProfanityFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.RegexFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.SpamFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.UnicodeFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.UrlFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.nuke.checks.*;
|
||||
import me.trouper.sentinel.server.gui.config.nuke.checks.command.DangerousCMDGUI;
|
||||
import me.trouper.sentinel.server.gui.config.nuke.checks.command.LoggedCMDGUI;
|
||||
@@ -18,19 +21,18 @@ import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ChatEvent implements CustomListener {
|
||||
|
||||
@EventHandler
|
||||
private void onChat(AsyncPlayerChatEvent e) {
|
||||
private void onChat(AsyncChatEvent e) {
|
||||
ServerUtils.verbose("Chat event sanity check:\n Canceled %s".formatted(e.isCancelled()));
|
||||
handleEvent(e);
|
||||
}
|
||||
|
||||
public static void handleEvent(AsyncPlayerChatEvent e) {
|
||||
public void handleEvent(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) return;
|
||||
if (PlayerUtils.isTrusted(e.getPlayer().getUniqueId().toString())) {
|
||||
if (MainGUI.awaitingCallback.contains(e.getPlayer().getUniqueId())) {
|
||||
@@ -41,7 +43,8 @@ public class ChatEvent implements CustomListener {
|
||||
|
||||
ServerUtils.verbose("Handling Chat Event for callbacks");
|
||||
SchedulerUtils.later(0,()->{
|
||||
RegexFilterGUI.updater.invokeCallbacks(e);
|
||||
UnicodeFilterGUI.updater.invokeCallbacks(e);
|
||||
UrlFilterGUI.updater.invokeCallbacks(e);
|
||||
ProfanityFilterGUI.updater.invokeCallbacks(e);
|
||||
SpamFilterGUI.updater.invokeCallbacks(e);
|
||||
DangerousCMDGUI.updater.invokeCallbacks(e);
|
||||
@@ -63,32 +66,40 @@ public class ChatEvent implements CustomListener {
|
||||
|
||||
handle(p,
|
||||
"sentinel.chat.regex.bypass",
|
||||
Sentinel.mainConfig.chat.useAntiUnicode, "unicode",
|
||||
Sentinel.mainConfig.chat.unicodeFilter.enabled, "unicode",
|
||||
e,
|
||||
AntiRegex::handleRegex);
|
||||
UnicodeFilter::handleUnicodeFilter);
|
||||
|
||||
ServerUtils.verbose("Chat event middle after regex:\n Canceled %s".formatted(e.isCancelled()));
|
||||
ServerUtils.verbose("Chat event middle after unicode:\n Canceled %s".formatted(e.isCancelled()));
|
||||
|
||||
handle(p,
|
||||
"sentinel.chat.regex.bypass",
|
||||
Sentinel.mainConfig.chat.unicodeFilter.enabled, "url",
|
||||
e,
|
||||
UrlFilter::handleUrlFilter);
|
||||
|
||||
ServerUtils.verbose("Chat event middle after unicode:\n Canceled %s".formatted(e.isCancelled()));
|
||||
|
||||
handle(p,
|
||||
"sentinel.chat.spam.bypass",
|
||||
Sentinel.mainConfig.chat.spamFilter.enabled,
|
||||
"spam",
|
||||
e,
|
||||
AntiSpam::handleAntiSpam);
|
||||
SpamFilter::handleSpamFilter);
|
||||
|
||||
ServerUtils.verbose("Chat event middle after spam:\n Canceled %s".formatted(e.isCancelled()));
|
||||
|
||||
handle(p,
|
||||
"sentinel.chat.swear.bypass",
|
||||
Sentinel.mainConfig.chat.swearFilter.enabled,
|
||||
Sentinel.mainConfig.chat.profanityFilter.enabled,
|
||||
"swear",
|
||||
e,
|
||||
AntiProfanity::handleProfanityFilter);
|
||||
ProfanityFilter::handleProfanityFilter);
|
||||
|
||||
ServerUtils.verbose("Chat event ending after swear:\n Canceled %s".formatted(e.isCancelled()));
|
||||
}
|
||||
|
||||
private static void handle(Player p, String permission, boolean isEnabled, String eventType, AsyncPlayerChatEvent e, Consumer<AsyncPlayerChatEvent> handler) {
|
||||
private static void handle(Player p, String permission, boolean isEnabled, String eventType, AsyncChatEvent e, Consumer<AsyncChatEvent> handler) {
|
||||
ServerUtils.verbose("Handeling a chat filter:\n Canceled %s\nType: %s".formatted(e.isCancelled(),eventType));
|
||||
if (e.isCancelled()) return;
|
||||
if (p.hasPermission(permission)) return;
|
||||
|
||||
@@ -2,10 +2,10 @@ package me.trouper.sentinel.server.events;
|
||||
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
@@ -3,8 +3,11 @@ package me.trouper.sentinel.server.events;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.ViolationController;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
import me.trouper.sentinel.utils.ItemUtils;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.utils.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.InventoryCreativeEvent;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.trouper.sentinel.server.events;
|
||||
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerCommandSendEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PluginCloakingEvent implements CustomListener {
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent e) {
|
||||
PluginCloakingPacket.tabReplaceQueue.remove(e.getPlayer());
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (!Sentinel.mainConfig.plugin.pluginHider) return;
|
||||
Player p = e.getPlayer();
|
||||
if (PlayerUtils.isTrusted(p)) return;
|
||||
|
||||
String message = e.getMessage();
|
||||
|
||||
if (message.startsWith("/")) {
|
||||
message = message.substring(1);
|
||||
}
|
||||
|
||||
for (String alias : Sentinel.advConfig.commandsWithPluginAccess) {
|
||||
if (!message.startsWith(alias)) continue;
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(Text.color(Sentinel.lang.permissions.noPlugins));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTabComplete(PlayerCommandSendEvent e) {
|
||||
if (!Sentinel.mainConfig.plugin.pluginHider) return;
|
||||
Player p = e.getPlayer();
|
||||
if (PlayerUtils.isTrusted(p)) return;
|
||||
|
||||
List<String> commands = e.getCommands().stream().toList();
|
||||
for (String command : commands) {
|
||||
if (command.contains(":")) {
|
||||
e.getCommands().remove(command);
|
||||
continue;
|
||||
}
|
||||
if (Sentinel.advConfig.commandsWithPluginAccess.contains(command)) {
|
||||
e.getCommands().remove(command);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ServerUtils.verbose("Removed all the plugin specific commands form the listing. It now contains %s".formatted(e.getCommands().stream().toList().toString()));
|
||||
for (String fakePlugin : Sentinel.advConfig.fakePlugins) {
|
||||
e.getCommands().add(fakePlugin + ":" + fakePlugin);
|
||||
}
|
||||
ServerUtils.verbose("Added the fake plugins, now it contains this: %s".formatted(e.getCommands().stream().toList().toString()));
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package me.trouper.sentinel.server.events;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerCommandSendEvent;
|
||||
import org.bukkit.event.server.TabCompleteEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PluginCloakingEvents implements CustomListener {
|
||||
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (!Sentinel.doNoPlugins) return;
|
||||
Player p = e.getPlayer();
|
||||
if (PlayerUtils.isTrusted(p)) return;
|
||||
|
||||
String message = e.getMessage();
|
||||
|
||||
if (message.startsWith("/")) {
|
||||
message = message.substring(1);
|
||||
}
|
||||
|
||||
for (String alias : Sentinel.advConfig.versionAliases) {
|
||||
if (!message.startsWith(alias)) continue;
|
||||
e.setCancelled(true);
|
||||
p.sendMessage(Text.color(Sentinel.lang.permissions.noPlugins));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTabComplete(PlayerCommandSendEvent e) {
|
||||
if (!Sentinel.doNoPlugins) return;
|
||||
Player p = e.getPlayer();
|
||||
if (PlayerUtils.isTrusted(p)) return;
|
||||
|
||||
List<String> commands = e.getCommands().stream().toList();
|
||||
for (String command : commands) {
|
||||
if (command.contains(":")) {
|
||||
e.getCommands().remove(command);
|
||||
continue;
|
||||
}
|
||||
if (Sentinel.advConfig.versionAliases.contains(command)) {
|
||||
e.getCommands().remove(command);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//ServerUtils.verbose("Removed all the plugin specific commands form the listing. It now contains %s".formatted(e.getCommands().stream().toList().toString()));
|
||||
for (String fakePlugin : Sentinel.advConfig.fakePlugins) {
|
||||
e.getCommands().add(fakePlugin + ":" + fakePlugin);
|
||||
}
|
||||
//ServerUtils.verbose("Added the fake plugins, now it contains this: %s".formatted(e.getCommands().stream().toList().toString()));
|
||||
}
|
||||
|
||||
public static void registerEvent(Plugin plugin) {
|
||||
Sentinel.protocolManager.addPacketListener(new PacketAdapter(
|
||||
plugin,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Client.TAB_COMPLETE
|
||||
) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
if (!Sentinel.doNoPlugins) return;
|
||||
if (PlayerUtils.isTrusted(event.getPlayer())) return;
|
||||
ServerUtils.verbose("Type: §b%s§7 Index 0: §b%s§7".formatted(
|
||||
event.getPacket().getType(),
|
||||
event.getPacket().getStrings().read(0)
|
||||
));
|
||||
|
||||
String command = event.getPacket().getStrings().read(0);
|
||||
if (command.startsWith("/")) command = command.replaceFirst("/","");
|
||||
ServerUtils.verbose("Command is " + command);
|
||||
for (String alias : Sentinel.advConfig.versionAliases) {
|
||||
alias = alias.trim();
|
||||
command = command.trim();
|
||||
if (alias.equals(command) || command.startsWith(alias)) {
|
||||
ServerUtils.verbose("Caught a command we can replace: %s".formatted(command));
|
||||
event.getPacket().getStrings().write(0,"/sentineltab ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Sentinel.protocolManager.addPacketListener(new PacketAdapter(
|
||||
plugin,
|
||||
ListenerPriority.NORMAL,
|
||||
PacketType.Play.Server.COMMANDS
|
||||
) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
if (PlayerUtils.isTrusted(event.getPlayer())) return;
|
||||
ServerUtils.verbose("Index 0: §b%s§7".formatted(
|
||||
event.getPacket().getStrings().read(0)
|
||||
));
|
||||
event.setCancelled(true);
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
String input = event.getPacket().getStrings().read(0);
|
||||
if (input == null || input.isEmpty()) return;
|
||||
|
||||
input = input.replaceFirst("/", "");
|
||||
if (input.length() < 2) {
|
||||
event.getPacket().getStrings().write(0, "sentineltab");
|
||||
ServerUtils.verbose("Successfully Blocked ver command: " + input);
|
||||
return;
|
||||
}
|
||||
|
||||
for (String ver : Sentinel.advConfig.versionAliases) {
|
||||
if (!input.startsWith(ver + " ")) continue;
|
||||
String modifiedInput = input.replaceFirst(ver, "sentineltab");
|
||||
event.getPacket().getStrings().write(0, modifiedInput);
|
||||
ServerUtils.verbose("Successfully Blocked ver command: " + input);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package me.trouper.sentinel.server.events;
|
||||
|
||||
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
|
||||
import com.github.retrooper.packetevents.event.PacketSendEvent;
|
||||
import com.github.retrooper.packetevents.protocol.chat.Node;
|
||||
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
|
||||
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientTabComplete;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDeclareCommands;
|
||||
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTabComplete;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PluginCloakingPacket extends PacketListenerAbstract {
|
||||
|
||||
public static final List<UUID> tabReplaceQueue = new ArrayList<>();
|
||||
|
||||
public PluginCloakingPacket() {
|
||||
super(PacketListenerPriority.NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (!Sentinel.mainConfig.plugin.pluginHider) return;
|
||||
if (event.getPacketType() != PacketType.Play.Client.TAB_COMPLETE) return;
|
||||
|
||||
WrapperPlayClientTabComplete wrapper = new WrapperPlayClientTabComplete(event);
|
||||
Player player = (Player) event.getPlayer();
|
||||
if (player == null) return;
|
||||
if (PlayerUtils.isTrusted(player)) return;
|
||||
|
||||
String text = wrapper.getText();
|
||||
for (String versionAlias : Sentinel.advConfig.pluginTabCompletions) {
|
||||
if (!text.contains(versionAlias)) continue;
|
||||
ServerUtils.verbose("Caught a version command tab completion. (%s -> %s)".formatted(text,versionAlias));
|
||||
tabReplaceQueue.add(player.getUniqueId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPacketSend(PacketSendEvent event) {
|
||||
if (!Sentinel.mainConfig.plugin.pluginHider) return;
|
||||
|
||||
Player player = (Player) event.getPlayer();
|
||||
if (player == null) return;
|
||||
if (PlayerUtils.isTrusted(player)) return;
|
||||
|
||||
switch (event.getPacketType()) {
|
||||
case PacketType.Play.Server.TAB_COMPLETE -> {
|
||||
if (tabReplaceQueue.contains(player.getUniqueId())) {
|
||||
tabReplaceQueue.remove(player.getUniqueId());
|
||||
ServerUtils.verbose("Player was queued for replacement, setting tab completions.");
|
||||
WrapperPlayServerTabComplete wrapper = new WrapperPlayServerTabComplete(event);
|
||||
List<WrapperPlayServerTabComplete.CommandMatch> matches = new ArrayList<>();
|
||||
for (String fakePlugin : Sentinel.advConfig.fakePlugins) {
|
||||
matches.add(new WrapperPlayServerTabComplete.CommandMatch(fakePlugin));
|
||||
}
|
||||
wrapper.setCommandMatches(matches);
|
||||
}
|
||||
}
|
||||
case PacketType.Play.Server.DECLARE_COMMANDS -> {
|
||||
WrapperPlayServerDeclareCommands wrapper = new WrapperPlayServerDeclareCommands(event);
|
||||
List<Node> nodes = wrapper.getNodes();
|
||||
for (Node node : nodes) {
|
||||
if (node.getName().isPresent() && node.getName().get().contains(":")) node.setName(Optional.of("sentineltab"));
|
||||
}
|
||||
wrapper.setNodes(nodes);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,25 @@
|
||||
package me.trouper.sentinel.server.functions;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.ServerUtils;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.commands.SocialSpyCommand;
|
||||
import me.trouper.sentinel.server.events.ChatEvent;
|
||||
import net.kyori.adventure.chat.SignedMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public class Message {
|
||||
public static final Map<UUID,UUID> replyMap = new HashMap<>();
|
||||
public static void messagePlayer(Player sender, Player receiver, String message) {
|
||||
HashSet<Player> receivers = new HashSet<>();
|
||||
receivers.add(receiver);
|
||||
receivers.add(sender);
|
||||
AsyncPlayerChatEvent checkEvent = new AsyncPlayerChatEvent(true,sender,message,receivers);
|
||||
AsyncChatEvent checkEvent = new AsyncChatEvent(true,sender, Set.of(receiver,sender), ChatRenderer.defaultRenderer(),Component.text(message),Component.text(message), SignedMessage.system(message,Component.text(message)));
|
||||
if (checkEvent.isCancelled()) return;
|
||||
ChatEvent.handleEvent(checkEvent);
|
||||
new ChatEvent().handleEvent(checkEvent);
|
||||
if (checkEvent.isCancelled()) return;
|
||||
|
||||
sender.sendMessage(Sentinel.lang.playerInteraction.messageSent.formatted(receiver.getName(),message));
|
||||
|
||||
6
src/main/java/me/trouper/sentinel/server/functions/ViolationController.java
Executable file → Normal file
6
src/main/java/me/trouper/sentinel/server/functions/ViolationController.java
Executable file → Normal file
@@ -1,13 +1,13 @@
|
||||
package me.trouper.sentinel.server.functions;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.utils.PlayerUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
|
||||
public abstract class AbstractActionHandler<T extends FilterResponse> {
|
||||
|
||||
public void run(T response) {
|
||||
FalsePositiveReporting.reports.put(response.getReport().getId(), response.getReport());
|
||||
Node tree = buildTree(response);
|
||||
|
||||
if (response.isBlocked()) {
|
||||
FilterHelpers.restrictMessage(response.getEvent(),!shouldWarnPlayer(response));
|
||||
}
|
||||
if (response.isPunished()) {
|
||||
punish(response);
|
||||
discordNotification(tree);
|
||||
}
|
||||
staffWarning(response, tree);
|
||||
if (shouldWarnPlayer(response)) {
|
||||
playerWarning(response);
|
||||
}
|
||||
consoleLog(tree);
|
||||
}
|
||||
|
||||
protected abstract void punish(T response);
|
||||
protected abstract void staffWarning(T response, Node tree);
|
||||
protected abstract void playerWarning(T response);
|
||||
protected abstract Node buildTree(T response);
|
||||
protected abstract boolean shouldWarnPlayer(T response);
|
||||
|
||||
protected void consoleLog(Node tree) {
|
||||
Sentinel.log.info(ConsoleFormatter.format(tree));
|
||||
}
|
||||
|
||||
protected void discordNotification(Node tree) {
|
||||
DiscordEmbed embed = EmbedFormatter.format(tree);
|
||||
EmbedFormatter.sendEmbed(embed);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package me.trouper.sentinel.server.functions.chatfilter;
|
||||
import io.github.itzispyder.pdk.utils.SchedulerUtils;
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.server.functions.Randomizer;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.Severity;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FilterHelpers {
|
||||
|
||||
public static Severity checkSlur(String text, Severity backup) {
|
||||
@@ -18,7 +22,11 @@ public class FilterHelpers {
|
||||
for (String swear : Sentinel.swearConfig.swears) {
|
||||
if (text.contains(swear)) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Pattern pattern = Pattern.compile(Sentinel.swearConfig.regexSwears, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
|
||||
return matcher.find() && Sentinel.swearConfig.useRegex;
|
||||
}
|
||||
|
||||
public static boolean containsSlurs(String text) {
|
||||
@@ -26,14 +34,18 @@ public class FilterHelpers {
|
||||
for (String slur : Sentinel.strictConfig.strict) {
|
||||
if (text.contains(slur)) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Pattern pattern = Pattern.compile(Sentinel.strictConfig.regexStrict, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
|
||||
return matcher.find() && Sentinel.strictConfig.useRegex;
|
||||
}
|
||||
|
||||
public static String removeFalsePositives(String text) {
|
||||
for (String falsePositive : Sentinel.fpConfig.swearWhitelist) {
|
||||
text = text.replace(falsePositive, "");
|
||||
}
|
||||
text = text.replaceAll(Sentinel.advConfig.falsePosRegex,"");
|
||||
text = text.replaceAll(Sentinel.fpConfig.regexWhitelist,"");
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -84,4 +96,12 @@ public class FilterHelpers {
|
||||
return FilterHelpers.removePeriodsAndSpaces(simplifiedText);
|
||||
}
|
||||
|
||||
public static void restrictMessage(AsyncChatEvent event, boolean silent) {
|
||||
if (silent) {
|
||||
event.viewers().clear();
|
||||
event.viewers().add(event.getPlayer());
|
||||
} else {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface FilterResponse {
|
||||
AsyncChatEvent getEvent();
|
||||
Player getPlayer();
|
||||
Report getReport();
|
||||
boolean isBlocked();
|
||||
boolean isPunished();
|
||||
}
|
||||
@@ -1,79 +1,59 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.profanity;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public class ProfanityAction {
|
||||
public static void run(ProfanityResponse response) {
|
||||
FalsePositiveReporting.reports.put(response.getReport().getId(),response.getReport());
|
||||
Node tree = getTree(response);
|
||||
public class ProfanityAction extends AbstractActionHandler<ProfanityResponse> {
|
||||
|
||||
if (response.isPunished()) {
|
||||
punish(response);
|
||||
discordNotification(tree);
|
||||
}
|
||||
staffWarning(response,tree);
|
||||
playerWarning(response);
|
||||
consoleLog(tree);
|
||||
}
|
||||
|
||||
public static void punish(ProfanityResponse response) {
|
||||
@Override
|
||||
public void punish(ProfanityResponse response) {
|
||||
if (response.getSeverity().equals(Severity.SLUR)) {
|
||||
for (String slurCommand : Sentinel.mainConfig.chat.swearFilter.strictPunishCommands) {
|
||||
ServerUtils.sendCommand(slurCommand.replaceAll("%player%", response.getEvent().getPlayer().getName()));
|
||||
for (String slurCommand : Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands) {
|
||||
ServerUtils.sendCommand(slurCommand.replaceAll("%player%", response.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
for (String swearCommand : Sentinel.mainConfig.chat.swearFilter.swearPunishCommands) {
|
||||
ServerUtils.sendCommand(swearCommand.replaceAll("%player%", response.getEvent().getPlayer().getName()));
|
||||
for (String swearCommand : Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands) {
|
||||
ServerUtils.sendCommand(swearCommand.replaceAll("%player%", response.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void staffWarning(ProfanityResponse report, Node tree) {
|
||||
@Override
|
||||
public void staffWarning(ProfanityResponse response, Node tree) {
|
||||
String messageText = Text.prefix("&b&n%s&r &7%s &8(&4%s&7/&c%s&8)".formatted(
|
||||
report.getEvent().getPlayer().getName(),
|
||||
report.isPunished() ? Sentinel.lang.violations.chat.profanity.autoPunish : Sentinel.lang.violations.chat.profanity.prevent,
|
||||
AntiProfanity.scoreMap.getOrDefault(report.getEvent().getPlayer().getUniqueId(), 0),
|
||||
Sentinel.mainConfig.chat.swearFilter.punishScore
|
||||
response.getPlayer().getName(),
|
||||
response.isPunished() ? Sentinel.lang.violations.chat.profanity.autoPunishNotification : Sentinel.lang.violations.chat.profanity.preventNotification,
|
||||
ProfanityFilter.scoreMap.getOrDefault(response.getPlayer().getUniqueId(), 0),
|
||||
Sentinel.mainConfig.chat.profanityFilter.punishScore
|
||||
));
|
||||
String hoverText = HoverFormatter.format(tree);
|
||||
|
||||
ServerUtils.forEachStaff(player -> player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())));
|
||||
}
|
||||
|
||||
public static void playerWarning(ProfanityResponse response) {
|
||||
String message = Text.prefix(!response.isPunished() ? Sentinel.lang.violations.chat.profanity.preventWarning : Sentinel.lang.violations.chat.profanity.autoPunishWarning);
|
||||
@Override
|
||||
public void playerWarning(ProfanityResponse response) {
|
||||
String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.profanity.autoPunishWarning : Sentinel.lang.violations.chat.profanity.preventWarning);
|
||||
String hoverText = Sentinel.lang.automatedActions.actionAutomaticReportable;
|
||||
String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId());
|
||||
response.getEvent().getPlayer().sendMessage(Component.text(message)
|
||||
response.getPlayer().sendMessage(Component.text(message)
|
||||
.hoverEvent(Component.text(hoverText).asHoverEvent())
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND,command)));
|
||||
}
|
||||
|
||||
public static void consoleLog(Node tree) {
|
||||
Sentinel.log.info(ConsoleFormatter.format(tree));
|
||||
}
|
||||
|
||||
public static void discordNotification(Node tree) {
|
||||
DiscordEmbed embed = EmbedFormatter.format(tree);
|
||||
EmbedFormatter.sendEmbed(embed);
|
||||
}
|
||||
|
||||
private static Node getTree(ProfanityResponse response) {
|
||||
@Override
|
||||
public Node buildTree(ProfanityResponse response) {
|
||||
Node root = new Node("Sentinel");
|
||||
root.addTextLine(Sentinel.lang.violations.chat.profanity.treeTitle);
|
||||
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.profanity.playerInfoTitle.formatted(response.getEvent().getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.uuid, response.getEvent().getPlayer().getUniqueId().toString());
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.score, "%s/%s".formatted(AntiProfanity.scoreMap.getOrDefault(response.getEvent().getPlayer().getUniqueId(),0),Sentinel.mainConfig.chat.swearFilter.punishScore));
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.profanity.playerInfoTitle.formatted(response.getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.uuid, response.getPlayer().getUniqueId().toString());
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.profanity.score, "%s/%s".formatted(ProfanityFilter.scoreMap.getOrDefault(response.getPlayer().getUniqueId(),0),Sentinel.mainConfig.chat.profanityFilter.punishScore));
|
||||
root.addChild(playerInfo);
|
||||
|
||||
Node reportInfo = new Node(Sentinel.lang.violations.chat.profanity.reportInfoTitle);
|
||||
@@ -89,4 +69,9 @@ public class ProfanityAction {
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldWarnPlayer(ProfanityResponse response) {
|
||||
return !Sentinel.mainConfig.chat.profanityFilter.silent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.profanity;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -9,22 +10,22 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AntiProfanity {
|
||||
public class ProfanityFilter {
|
||||
public static Map<UUID, Integer> scoreMap = new HashMap<>();
|
||||
|
||||
public static void handleProfanityFilter(AsyncPlayerChatEvent event) {
|
||||
public static void handleProfanityFilter(AsyncChatEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
ServerUtils.verbose("Anti Profanity Opening: Event is canceled.");
|
||||
}
|
||||
Player player = event.getPlayer();
|
||||
ProfanityResponse response = ProfanityResponse.generate(event);
|
||||
Severity severity = response.getSeverity();
|
||||
ServerUtils.verbose("Response came back, uncertain null.");
|
||||
ServerUtils.verbose("Response came back.");
|
||||
if (severity == null) return;
|
||||
ServerUtils.verbose("Not null, its severity is " + severity);
|
||||
if (severity.equals(Severity.SAFE)) return;
|
||||
ServerUtils.verbose("Not null or safe, canceling event");
|
||||
event.setCancelled(true);
|
||||
response.setBlocked(true);
|
||||
|
||||
scoreMap.putIfAbsent(player.getUniqueId(), 0);
|
||||
int previousScore = scoreMap.get(player.getUniqueId());
|
||||
@@ -32,20 +33,20 @@ public class AntiProfanity {
|
||||
int newScore = previousScore + severity.getScore();
|
||||
scoreMap.put(player.getUniqueId(), newScore);
|
||||
|
||||
if (newScore > Sentinel.mainConfig.chat.swearFilter.punishScore || Severity.SLUR.equals(severity)) {
|
||||
if (newScore > Sentinel.mainConfig.chat.profanityFilter.punishScore || Severity.SLUR.equals(severity)) {
|
||||
response.setPunished(true);
|
||||
ProfanityAction.run(response);
|
||||
new ProfanityAction().run(response);
|
||||
return;
|
||||
}
|
||||
|
||||
ProfanityAction.run(response);
|
||||
new ProfanityAction().run(response);
|
||||
}
|
||||
|
||||
public static void decayScore() {
|
||||
for (UUID uuid : scoreMap.keySet()) {
|
||||
int score = scoreMap.get(uuid);
|
||||
if (score > 0) {
|
||||
score = score - Sentinel.mainConfig.chat.swearFilter.scoreDecay;
|
||||
score = score - Sentinel.mainConfig.chat.profanityFilter.scoreDecay;
|
||||
scoreMap.put(uuid, Math.max(0, score));
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,47 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.profanity;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FilterHelpers;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FilterResponse;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.Report;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
public class ProfanityResponse {
|
||||
public class ProfanityResponse implements FilterResponse {
|
||||
|
||||
private AsyncPlayerChatEvent event;
|
||||
private AsyncChatEvent event;
|
||||
private String originalMessage;
|
||||
private String processedMessage;
|
||||
private Report report;
|
||||
private Severity severity;
|
||||
private boolean blocked;
|
||||
private boolean punished;
|
||||
|
||||
public ProfanityResponse(AsyncPlayerChatEvent event, String originalMessage, String processedMessage, Report report, Severity severity, boolean punished) {
|
||||
public ProfanityResponse(AsyncChatEvent event, String originalMessage, String processedMessage, Report report, Severity severity, boolean blocked, boolean punished) {
|
||||
this.event = event;
|
||||
this.originalMessage = originalMessage;
|
||||
this.processedMessage = processedMessage;
|
||||
this.report = report;
|
||||
this.severity = severity;
|
||||
this.blocked = blocked;
|
||||
this.punished = punished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return event.getPlayer();
|
||||
}
|
||||
|
||||
public AsyncPlayerChatEvent getEvent() {
|
||||
public AsyncChatEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(AsyncPlayerChatEvent event) {
|
||||
public void setEvent(AsyncChatEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@@ -66,6 +77,14 @@ public class ProfanityResponse {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public boolean isPunished() {
|
||||
return punished;
|
||||
}
|
||||
@@ -74,16 +93,18 @@ public class ProfanityResponse {
|
||||
this.punished = punished;
|
||||
}
|
||||
|
||||
public static ProfanityResponse generate(AsyncPlayerChatEvent e) {
|
||||
public static ProfanityResponse generate(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Profanity response opening Event is canceled.");
|
||||
ServerUtils.verbose("Profanity response opening: Event is canceled.");
|
||||
}
|
||||
Report report = FalsePositiveReporting.initializeReport(e.getMessage());
|
||||
|
||||
String message = LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
Report report = FalsePositiveReporting.initializeReport(message);
|
||||
Severity severity = Severity.SAFE;
|
||||
|
||||
ProfanityResponse response = new ProfanityResponse(e,e.getMessage(),null,report,severity,false);
|
||||
ProfanityResponse response = new ProfanityResponse(e,message,null,report,severity,false,false);
|
||||
|
||||
String text = Text.removeFirstColor(e.getMessage());
|
||||
String text = Text.removeFirstColor(message);
|
||||
response.setOriginalMessage(text);
|
||||
|
||||
// 1:
|
||||
|
||||
@@ -3,13 +3,13 @@ package me.trouper.sentinel.server.functions.chatfilter.profanity;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
|
||||
public enum Severity {
|
||||
LOW(Sentinel.mainConfig.chat.swearFilter.lowScore),
|
||||
MEDIUM_LOW(Sentinel.mainConfig.chat.swearFilter.mediumLowScore),
|
||||
MEDIUM(Sentinel.mainConfig.chat.swearFilter.mediumScore),
|
||||
MEDIUM_HIGH(Sentinel.mainConfig.chat.swearFilter.mediumHighScore),
|
||||
HIGH(Sentinel.mainConfig.chat.swearFilter.highScore),
|
||||
REGEX(Sentinel.mainConfig.chat.swearFilter.regexScore),
|
||||
SLUR(Sentinel.mainConfig.chat.swearFilter.highScore),
|
||||
LOW(Sentinel.mainConfig.chat.profanityFilter.lowScore),
|
||||
MEDIUM_LOW(Sentinel.mainConfig.chat.profanityFilter.mediumLowScore),
|
||||
MEDIUM(Sentinel.mainConfig.chat.profanityFilter.mediumScore),
|
||||
MEDIUM_HIGH(Sentinel.mainConfig.chat.profanityFilter.mediumHighScore),
|
||||
HIGH(Sentinel.mainConfig.chat.profanityFilter.highScore),
|
||||
REGEX(Sentinel.mainConfig.chat.profanityFilter.regexScore),
|
||||
SLUR(Sentinel.mainConfig.chat.profanityFilter.highScore),
|
||||
SAFE(0);
|
||||
|
||||
private final int score;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.regex;
|
||||
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
public class AntiRegex {
|
||||
public static void handleRegex(AsyncPlayerChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Regex Filter opening Event is canceled.");
|
||||
}
|
||||
ServerUtils.verbose("Handeling advanced");
|
||||
RegexResponse response = RegexResponse.generate(e);
|
||||
ServerUtils.verbose("Response got back");
|
||||
if (response.getFlagType() == null) return;
|
||||
ServerUtils.verbose("Detection happened");
|
||||
e.setCancelled(true);
|
||||
RegexAction.run(response);
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.regex;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.AntiProfanity;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public class RegexAction {
|
||||
public static void run(RegexResponse response) {
|
||||
FalsePositiveReporting.reports.put(response.getReport().getId(),response.getReport());
|
||||
Node tree = getTree(response);
|
||||
|
||||
if (response.isPunished()) {
|
||||
punish(response);
|
||||
discordNotification(tree);
|
||||
}
|
||||
staffWarning(response,tree);
|
||||
playerWarning(response);
|
||||
consoleLog(tree);
|
||||
}
|
||||
|
||||
public static void punish(RegexResponse response) {
|
||||
if (response.getFlagType().equals(RegexFlagType.STRICT_BLOCK)) {
|
||||
for (String slurCommand : Sentinel.mainConfig.chat.swearFilter.strictPunishCommands) {
|
||||
ServerUtils.sendCommand(slurCommand.replaceAll("%player%", response.getEvent().getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
for (String swearCommand : Sentinel.mainConfig.chat.swearFilter.swearPunishCommands) {
|
||||
ServerUtils.sendCommand(swearCommand.replaceAll("%player%", response.getEvent().getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void staffWarning(RegexResponse report, Node tree) {
|
||||
String messageText = Text.prefix("&b&n%s&r &7%s".formatted(
|
||||
report.getEvent().getPlayer().getName(),
|
||||
report.isPunished() ? Sentinel.lang.violations.chat.regex.autoPunish : Sentinel.lang.violations.chat.regex.regexTrigger
|
||||
));
|
||||
String hoverText = HoverFormatter.format(tree);
|
||||
|
||||
ServerUtils.forEachStaff(player -> player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())));
|
||||
}
|
||||
|
||||
public static void playerWarning(RegexResponse response) {
|
||||
String message = Text.prefix(response.getFlagType().getBlockMessage()) ;
|
||||
String hoverText = Sentinel.lang.automatedActions.actionAutomaticReportable;
|
||||
String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId());
|
||||
response.getEvent().getPlayer().sendMessage(Component.text(message)
|
||||
.hoverEvent(Component.text(hoverText).asHoverEvent())
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND,command)));
|
||||
}
|
||||
|
||||
public static void consoleLog(Node tree) {
|
||||
Sentinel.log.info(ConsoleFormatter.format(tree));
|
||||
}
|
||||
|
||||
public static void discordNotification(Node tree) {
|
||||
DiscordEmbed embed = EmbedFormatter.format(tree);
|
||||
EmbedFormatter.sendEmbed(embed);
|
||||
}
|
||||
|
||||
private static Node getTree(RegexResponse response) {
|
||||
Node root = new Node("Sentinel");
|
||||
root.addTextLine(Sentinel.lang.violations.chat.regex.treeTitle);
|
||||
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.regex.playerInfoTitle.formatted(response.getEvent().getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.regex.uuid, response.getEvent().getPlayer().getUniqueId().toString());
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.regex.score, "%s".formatted(AntiProfanity.scoreMap.get(response.getEvent().getPlayer().getUniqueId())));
|
||||
root.addChild(playerInfo);
|
||||
|
||||
Node reportInfo = new Node(Sentinel.lang.violations.chat.regex.reportInfoTitle.formatted(response.getFlagType().getName()));
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.regex.originalMessage, response.getOriginalMessage());
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.regex.flaggedMessage, response.getHighlightedMessage());
|
||||
root.addChild(reportInfo);
|
||||
|
||||
Node actions = new Node(Sentinel.lang.violations.chat.regex.actionTitle);
|
||||
actions.addTextLine(Sentinel.lang.violations.chat.regex.blockAction);
|
||||
if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.chat.regex.commandAction);
|
||||
root.addChild(actions);
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.regex;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
|
||||
public enum RegexFlagType {
|
||||
URL_BLOCK(Sentinel.lang.violations.chat.regex.urlBlockName, Sentinel.lang.violations.chat.regex.urlBlockMessage),
|
||||
UNICODE_BLOCK(Sentinel.lang.violations.chat.regex.unicodeBlockName, Sentinel.lang.violations.chat.regex.unicodeBlockMessage),
|
||||
SWEAR_BLOCK(Sentinel.lang.violations.chat.regex.swearBlockName, Sentinel.lang.violations.chat.regex.swearBlockMessage),
|
||||
STRICT_BLOCK(Sentinel.lang.violations.chat.regex.strictBlockName, Sentinel.lang.violations.chat.regex.strictBlockMessage);
|
||||
|
||||
|
||||
|
||||
private String name;
|
||||
private String blockMessage;
|
||||
|
||||
RegexFlagType(String name, String blockMessage) {
|
||||
|
||||
}
|
||||
|
||||
public String getBlockMessage() {
|
||||
return blockMessage;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.regex;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.Report;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RegexResponse {
|
||||
private AsyncPlayerChatEvent event;
|
||||
private String originalMessage;
|
||||
private String highlightedMessage;
|
||||
private RegexFlagType flagType;
|
||||
private Report report;
|
||||
private boolean isPunished;
|
||||
|
||||
public RegexResponse(AsyncPlayerChatEvent event, String originalMessage, String highlightedMessage, RegexFlagType flagType, Report report, boolean isPunished) {
|
||||
this.event = event;
|
||||
this.originalMessage = originalMessage;
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
this.flagType = flagType;
|
||||
this.report = report;
|
||||
this.isPunished = isPunished;
|
||||
}
|
||||
|
||||
public AsyncPlayerChatEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(AsyncPlayerChatEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
public String getOriginalMessage() {
|
||||
return originalMessage;
|
||||
}
|
||||
|
||||
public void setOriginalMessage(String originalMessage) {
|
||||
this.originalMessage = originalMessage;
|
||||
}
|
||||
|
||||
public String getHighlightedMessage() {
|
||||
return highlightedMessage;
|
||||
}
|
||||
|
||||
public void setHighlightedMessage(String highlightedMessage) {
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
}
|
||||
|
||||
public RegexFlagType getFlagType() {
|
||||
return flagType;
|
||||
}
|
||||
|
||||
public void setFlagType(RegexFlagType flagType) {
|
||||
this.flagType = flagType;
|
||||
}
|
||||
|
||||
public Report getReport() {
|
||||
return report;
|
||||
}
|
||||
|
||||
public void setReport(Report report) {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public boolean isPunished() {
|
||||
return isPunished;
|
||||
}
|
||||
|
||||
public void setPunished(boolean punished) {
|
||||
isPunished = punished;
|
||||
}
|
||||
|
||||
|
||||
public static RegexResponse generate(AsyncPlayerChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Regex response opening Event is canceled.");
|
||||
}
|
||||
Report report = FalsePositiveReporting.initializeReport(e.getMessage());
|
||||
RegexResponse response = new RegexResponse(e,e.getMessage(),null,null,report,false);
|
||||
|
||||
String unicode = handleAntiUnicode(e,response.getReport());
|
||||
String url = handleAntiURL(e,response.getReport());
|
||||
String strict = handleStrictRegex(e,response.getReport());
|
||||
String swear = handleSwearRegex(e,response.getReport());
|
||||
ServerUtils.verbose("All filters have ran without thread error");
|
||||
if (Sentinel.mainConfig.chat.useAntiUnicode && handleAntiUnicode(e,report) != null) {
|
||||
response.setHighlightedMessage(unicode);
|
||||
response.setFlagType(RegexFlagType.UNICODE_BLOCK);
|
||||
return response;
|
||||
}
|
||||
if (Sentinel.mainConfig.chat.useAntiURL && handleAntiURL(e,report) != null) {
|
||||
response.setHighlightedMessage(url);
|
||||
response.setFlagType(RegexFlagType.URL_BLOCK);
|
||||
return response;
|
||||
}
|
||||
if (Sentinel.mainConfig.chat.useStrictRegex && handleStrictRegex(e,report) != null) {
|
||||
response.setHighlightedMessage(strict);
|
||||
response.setFlagType(RegexFlagType.STRICT_BLOCK);
|
||||
return response;
|
||||
}
|
||||
if (Sentinel.mainConfig.chat.useSwearRegex && handleSwearRegex(e,report) != null) {
|
||||
response.setPunished(true);
|
||||
response.setHighlightedMessage(swear);
|
||||
response.setFlagType(RegexFlagType.SWEAR_BLOCK);
|
||||
return response;
|
||||
}
|
||||
ServerUtils.verbose("Nothing caught, returning the blank response");
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
public static String handleAntiUnicode(AsyncPlayerChatEvent e, Report report) {
|
||||
String message = Text.removeFirstColor(e.getMessage());
|
||||
report.getStepsTaken().put("Anti-Unicode", "`%s`".formatted(message));
|
||||
ServerUtils.verbose("AdvBlocker: Checking for unicode: " + message);
|
||||
String nonAllowed = message.replaceAll(Sentinel.advConfig.allowedCharRegex, "").trim();
|
||||
|
||||
if (!nonAllowed.isEmpty()) {
|
||||
ServerUtils.verbose("AdvBlocker: Caught Unicode: " + nonAllowed);
|
||||
report.getStepsTaken().replace("Anti-Unicode", "`%s` %s".formatted(message, Emojis.alarm));
|
||||
return Text.regexHighlighter(message,Sentinel.advConfig.allowedCharRegex," > ", " < ");
|
||||
}
|
||||
ServerUtils.verbose("Nothing caught, returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String handleSwearRegex(AsyncPlayerChatEvent e, Report report) {
|
||||
String swearRegex = Sentinel.advConfig.swearRegex;
|
||||
|
||||
Pattern pattern = Pattern.compile(swearRegex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(e.getMessage());
|
||||
|
||||
report.getStepsTaken().put("Anti-Swear Regex", "`%s`".formatted(e.getMessage()));
|
||||
|
||||
if (matcher.find()) {
|
||||
String highlighted = Text.regexHighlighter(swearRegex,e.getMessage()," > "," < ");
|
||||
report.getStepsTaken().replace("Anti-Swear Regex", "`%s` %s".formatted(highlighted, Emojis.alarm));
|
||||
return highlighted;
|
||||
}
|
||||
ServerUtils.verbose("Nothing caught, returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String handleStrictRegex(AsyncPlayerChatEvent e, Report report) {
|
||||
String strictRegex = Sentinel.advConfig.strictRegex;
|
||||
|
||||
Pattern pattern = Pattern.compile(strictRegex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(e.getMessage());
|
||||
|
||||
report.getStepsTaken().put("Strict Regex", "`%s`".formatted(e.getMessage()));
|
||||
|
||||
|
||||
|
||||
if (matcher.find()) {
|
||||
String highlighted = Text.regexHighlighter(strictRegex,e.getMessage()," > "," < ");
|
||||
report.getStepsTaken().replace("Strict Regex", "`%s` %s".formatted(highlighted, Emojis.alarm));
|
||||
return highlighted;
|
||||
}
|
||||
ServerUtils.verbose("Nothing caught, returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String handleAntiURL(AsyncPlayerChatEvent e, Report report) {
|
||||
String urlRegex = Sentinel.advConfig.urlRegex;
|
||||
|
||||
Pattern pattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(e.getMessage());
|
||||
//ServerUtils.sendDebugMessage("AdvBlocker: Checking for URLs against regex `%1$s`:%2$s".formatted(urlRegex, e.getMessage()));
|
||||
|
||||
report.getStepsTaken().put("Anti-URL", "`%s`".formatted(
|
||||
e.getMessage()
|
||||
));
|
||||
|
||||
if (matcher.find()) {
|
||||
String highlighted = Text.regexHighlighter(e.getMessage(),Sentinel.advConfig.urlRegex," > "," < ");
|
||||
ServerUtils.verbose("AdvBlocker: Caught URL: " + highlighted);
|
||||
report.getStepsTaken().replace("Anti-URL", "`%s` %s".formatted(highlighted, Emojis.alarm));
|
||||
return highlighted;
|
||||
}
|
||||
ServerUtils.verbose("Nothing caught, returning null");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,29 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.spam;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.utils.trees.ConsoleFormatter;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public class SpamAction {
|
||||
public class SpamAction extends AbstractActionHandler<SpamResponse> {
|
||||
|
||||
public static void run(SpamResponse response) {
|
||||
FalsePositiveReporting.reports.put(response.getReport().getId(),response.getReport());
|
||||
Node tree = getTree(response);
|
||||
|
||||
if (response.isPunished()) {
|
||||
punish(response);
|
||||
discordNotification(tree);
|
||||
}
|
||||
staffWarning(response,tree);
|
||||
playerWarning(response);
|
||||
consoleLog(tree);
|
||||
|
||||
}
|
||||
|
||||
public static void punish(SpamResponse response) {
|
||||
@Override
|
||||
public void punish(SpamResponse response) {
|
||||
for (String spamPunishCommand : Sentinel.mainConfig.chat.spamFilter.punishCommands) {
|
||||
ServerUtils.sendCommand(spamPunishCommand.replaceAll("%player%", response.getEvent().getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void staffWarning(SpamResponse report, Node tree) {
|
||||
@Override
|
||||
public void staffWarning(SpamResponse report, Node tree) {
|
||||
String messageText = Text.prefix("&b&n%s&r &7%s &8(&4%s&7/&c%s&8)".formatted(
|
||||
report.getEvent().getPlayer().getName(),
|
||||
report.isPunished() ? Sentinel.lang.violations.chat.spam.autoPunish : Sentinel.lang.violations.chat.spam.spamWarning,
|
||||
AntiSpam.heatMap.get(report.getEvent().getPlayer().getUniqueId()),
|
||||
report.isPunished() ? Sentinel.lang.violations.chat.spam.autoPunishNotification : Sentinel.lang.violations.chat.spam.preventNotification,
|
||||
SpamFilter.heatMap.get(report.getEvent().getPlayer().getUniqueId()),
|
||||
Sentinel.mainConfig.chat.spamFilter.punishHeat
|
||||
));
|
||||
String hoverText = HoverFormatter.format(tree);
|
||||
@@ -46,8 +31,9 @@ public class SpamAction {
|
||||
ServerUtils.forEachStaff(player -> player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())));
|
||||
}
|
||||
|
||||
public static void playerWarning(SpamResponse response) {
|
||||
String message = Text.prefix(!response.isPunished() ? Sentinel.lang.violations.chat.spam.preventWarning : Sentinel.lang.violations.chat.spam.autoPunishWarning) ;
|
||||
@Override
|
||||
public void playerWarning(SpamResponse response) {
|
||||
String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.spam.autoPunishWarning : Sentinel.lang.violations.chat.spam.preventWarning) ;
|
||||
String hoverText = Sentinel.lang.automatedActions.actionAutomaticReportable;
|
||||
String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId());
|
||||
response.getEvent().getPlayer().sendMessage(Component.text(message)
|
||||
@@ -55,22 +41,14 @@ public class SpamAction {
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND,command)));
|
||||
}
|
||||
|
||||
public static void consoleLog(Node tree) {
|
||||
Sentinel.log.info(ConsoleFormatter.format(tree));
|
||||
}
|
||||
|
||||
public static void discordNotification(Node tree) {
|
||||
DiscordEmbed embed = EmbedFormatter.format(tree);
|
||||
EmbedFormatter.sendEmbed(embed);
|
||||
}
|
||||
|
||||
private static Node getTree(SpamResponse response) {
|
||||
@Override
|
||||
public Node buildTree(SpamResponse response) {
|
||||
Node root = new Node("Sentinel");
|
||||
root.addTextLine(Sentinel.lang.violations.chat.spam.treeTitle);
|
||||
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.spam.playerInfoTitle.formatted(response.getEvent().getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.spam.uuid, response.getEvent().getPlayer().getUniqueId().toString());
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.spam.heat, "%s/%s".formatted(AntiSpam.heatMap.get(response.getEvent().getPlayer().getUniqueId()),Sentinel.mainConfig.chat.spamFilter.punishHeat));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.spam.heat, "%s/%s".formatted(SpamFilter.heatMap.get(response.getEvent().getPlayer().getUniqueId()),Sentinel.mainConfig.chat.spamFilter.punishHeat));
|
||||
root.addChild(playerInfo);
|
||||
|
||||
Node reportInfo = new Node(Sentinel.lang.violations.chat.spam.reportInfoTitle);
|
||||
@@ -86,4 +64,9 @@ public class SpamAction {
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldWarnPlayer(SpamResponse response) {
|
||||
return !Sentinel.mainConfig.chat.spamFilter.silent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.spam;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AntiSpam {
|
||||
public class SpamFilter {
|
||||
public static Map<UUID, Integer> heatMap = new HashMap<>();
|
||||
public static Map<UUID, String> lastMessageMap = new HashMap<>();
|
||||
|
||||
public static void handleAntiSpam(AsyncPlayerChatEvent e) {
|
||||
public static void handleSpamFilter(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Anti Spam Opening: Event is canceled.");
|
||||
}
|
||||
Player p = e.getPlayer();
|
||||
String message = Text.removeFirstColor(e.getMessage());
|
||||
String message = Text.removeFirstColor(LegacyComponentSerializer.legacySection().serialize(e.message()));
|
||||
for (String whitelistedMessage : Sentinel.mainConfig.chat.spamFilter.whitelist) {
|
||||
if (whitelistedMessage.equalsIgnoreCase(message)) return;
|
||||
}
|
||||
int currentHeat = heatMap.getOrDefault(p.getUniqueId(),0);
|
||||
|
||||
SpamResponse response = SpamResponse.generate(e);
|
||||
lastMessageMap.put(e.getPlayer().getUniqueId(), e.getMessage());
|
||||
lastMessageMap.put(e.getPlayer().getUniqueId(), message);
|
||||
|
||||
int addHeat = response.getHeatAdded();
|
||||
|
||||
@@ -31,26 +35,26 @@ public class AntiSpam {
|
||||
response.getReport().getStepsTaken().put("Response came back", "Heat to add: %s".formatted(addHeat));
|
||||
|
||||
if (currentHeat > Sentinel.mainConfig.chat.spamFilter.punishHeat) {
|
||||
e.setCancelled(true);
|
||||
response.setBlocked(true);
|
||||
response.getReport().getStepsTaken().put("Punished user", "Their final heat was %s".formatted(currentHeat));
|
||||
response.setPunished(true);
|
||||
SpamAction.run(response);
|
||||
new SpamAction().run(response);
|
||||
heatMap.put(p.getUniqueId(), currentHeat + addHeat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentHeat > Sentinel.mainConfig.chat.spamFilter.blockHeat) {
|
||||
e.setCancelled(true);
|
||||
response.setBlocked(true);
|
||||
response.getReport().getStepsTaken().put("Blocked message", "Their heat is %s".formatted(currentHeat));
|
||||
SpamAction.run(response);
|
||||
new SpamAction().run(response);
|
||||
heatMap.put(p.getUniqueId(), currentHeat + addHeat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.getSimilarity() > Sentinel.mainConfig.chat.spamFilter.blockSimilarity) {
|
||||
e.setCancelled(true);
|
||||
response.setBlocked(true);
|
||||
response.getReport().getStepsTaken().put("Blocked message", "The similarity was too high! %s".formatted(response.getSimilarity()));
|
||||
SpamAction.run(response);
|
||||
new SpamAction().run(response);
|
||||
heatMap.put(p.getUniqueId(), currentHeat + addHeat);
|
||||
return;
|
||||
}
|
||||
@@ -1,44 +1,52 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.spam;
|
||||
|
||||
import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FilterResponse;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.Report;
|
||||
import me.trouper.sentinel.utils.MathUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import static me.trouper.sentinel.server.functions.chatfilter.spam.AntiSpam.lastMessageMap;
|
||||
import static me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter.lastMessageMap;
|
||||
|
||||
public class SpamResponse {
|
||||
private AsyncPlayerChatEvent event;
|
||||
public class SpamResponse implements FilterResponse {
|
||||
private AsyncChatEvent event;
|
||||
private String currentMessage;
|
||||
private String previousMessage;
|
||||
private double similarity;
|
||||
private int heatAdded;
|
||||
private Report report;
|
||||
private boolean blocked;
|
||||
private boolean punished;
|
||||
|
||||
public SpamResponse(AsyncPlayerChatEvent event, String currentMessage, String previousMessage, double similarity, int heatAdded, Report report, boolean punished) {
|
||||
public SpamResponse(AsyncChatEvent event, String currentMessage, String previousMessage, double similarity, int heatAdded, Report report, boolean blocked, boolean punished) {
|
||||
this.event = event;
|
||||
this.currentMessage = currentMessage;
|
||||
this.previousMessage = previousMessage;
|
||||
this.similarity = similarity;
|
||||
this.heatAdded = heatAdded;
|
||||
this.report = report;
|
||||
this.blocked = blocked;
|
||||
this.punished = punished;
|
||||
}
|
||||
|
||||
public static SpamResponse generate(AsyncPlayerChatEvent e) {
|
||||
public static SpamResponse generate(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Spam response opening: Event is canceled.");
|
||||
}
|
||||
Report report = FalsePositiveReporting.initializeReport(e.getMessage());
|
||||
|
||||
String message = Text.removeFirstColor(e.getMessage());
|
||||
String message = LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
Report report = FalsePositiveReporting.initializeReport(message);
|
||||
|
||||
message = Text.removeFirstColor(message);
|
||||
String previousMessage = lastMessageMap.getOrDefault(e.getPlayer().getUniqueId(),"/* Placeholder Message from Sentinel */");
|
||||
|
||||
SpamResponse response = new SpamResponse(e,e.getMessage(),previousMessage,0,0,report,false);
|
||||
SpamResponse response = new SpamResponse(e,message,previousMessage,0,0,report,false,false);
|
||||
|
||||
|
||||
double similarity = MathUtils.calcSim(message, previousMessage);
|
||||
@@ -76,11 +84,11 @@ public class SpamResponse {
|
||||
return response;
|
||||
}
|
||||
|
||||
public AsyncPlayerChatEvent getEvent() {
|
||||
public AsyncChatEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(AsyncPlayerChatEvent event) {
|
||||
public void setEvent(AsyncChatEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@@ -116,6 +124,11 @@ public class SpamResponse {
|
||||
this.heatAdded = heatAdded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return event.getPlayer();
|
||||
}
|
||||
|
||||
public Report getReport() {
|
||||
return report;
|
||||
}
|
||||
@@ -124,6 +137,14 @@ public class SpamResponse {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public boolean isPunished() {
|
||||
return punished;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.unicode;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public class UnicodeAction extends AbstractActionHandler<UnicodeResponse> {
|
||||
@Override
|
||||
protected void punish(UnicodeResponse response) {
|
||||
for (String punishCommand : Sentinel.mainConfig.chat.unicodeFilter.punishCommands) {
|
||||
ServerUtils.sendCommand(punishCommand.replaceAll("%player%",response.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void staffWarning(UnicodeResponse response, Node tree) {
|
||||
String messageText = Text.prefix("&b&n%s&r &7%s".formatted(
|
||||
response.getPlayer().getName(),
|
||||
response.isPunished() ? Sentinel.lang.violations.chat.unicode.autoPunishNotification : Sentinel.lang.violations.chat.unicode.preventNotification
|
||||
));
|
||||
String hoverText = HoverFormatter.format(tree);
|
||||
|
||||
ServerUtils.forEachStaff(player -> player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playerWarning(UnicodeResponse response) {
|
||||
String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.unicode.autoPunishWarning : Sentinel.lang.violations.chat.unicode.preventWarning);
|
||||
String hoverText = Sentinel.lang.automatedActions.actionAutomaticReportable;
|
||||
String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId());
|
||||
response.getPlayer().sendMessage(Component.text(message)
|
||||
.hoverEvent(Component.text(hoverText).asHoverEvent())
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND,command)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node buildTree(UnicodeResponse response) {
|
||||
Node root = new Node("Sentinel");
|
||||
root.addTextLine(Sentinel.lang.violations.chat.unicode.treeTitle);
|
||||
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.unicode.playerInfoTitle.formatted(response.getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.unicode.uuid, response.getPlayer().getUniqueId().toString());
|
||||
root.addChild(playerInfo);
|
||||
|
||||
Node reportInfo = new Node(Sentinel.lang.violations.chat.unicode.reportInfoTitle);
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.unicode.originalMessage, response.getOriginalMessage());
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.unicode.highlightedMessage, response.getHighlightedMessage());
|
||||
root.addChild(reportInfo);
|
||||
|
||||
Node actions = new Node(Sentinel.lang.violations.chat.unicode.actionTitle);
|
||||
actions.addTextLine(Sentinel.lang.violations.chat.unicode.blockAction);
|
||||
if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.chat.unicode.commandAction);
|
||||
root.addChild(actions);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldWarnPlayer(UnicodeResponse response) {
|
||||
return !Sentinel.mainConfig.chat.unicodeFilter.silent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.unicode;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
|
||||
public class UnicodeFilter {
|
||||
|
||||
public static void handleUnicodeFilter(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Unicode Filter Opening: Event is canceled.");
|
||||
}
|
||||
|
||||
UnicodeResponse response = UnicodeResponse.generate(e);
|
||||
|
||||
if (response.isPunished() || response.isBlocked()) new UnicodeAction().run(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.unicode;
|
||||
|
||||
import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FilterResponse;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.Report;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UnicodeResponse implements FilterResponse {
|
||||
|
||||
private AsyncChatEvent event;
|
||||
private String originalMessage;
|
||||
private String highlightedMessage;
|
||||
private Report report;
|
||||
private boolean blocked;
|
||||
private boolean punished;
|
||||
|
||||
public UnicodeResponse(AsyncChatEvent event, String originalMessage, String highlightedMessage, Report report, boolean blocked, boolean punished) {
|
||||
this.event = event;
|
||||
this.report = report;
|
||||
this.originalMessage = originalMessage;
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
this.blocked = blocked;
|
||||
this.punished = punished;
|
||||
}
|
||||
|
||||
public static UnicodeResponse generate(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Unicode response opening: Event is canceled.");
|
||||
}
|
||||
|
||||
String message = LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
message = Text.removeFirstColor(message);
|
||||
Report report = FalsePositiveReporting.initializeReport(message);
|
||||
|
||||
UnicodeResponse response = new UnicodeResponse(e,message,message,report,false,false);
|
||||
|
||||
String disallowedRegex = Sentinel.mainConfig.chat.unicodeFilter.regex;
|
||||
ServerUtils.verbose("Regex: %s\nMessage: %s".formatted(disallowedRegex,message));
|
||||
|
||||
Pattern pattern = Pattern.compile(disallowedRegex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(message);
|
||||
ServerUtils.verbose("Matcher result %s\nGroup 1: %s".formatted(matcher.find(),matcher.find() ? matcher.group() : ""));
|
||||
|
||||
response.getReport().getStepsTaken().put("Anti-URL", "`%s`".formatted(
|
||||
message
|
||||
));
|
||||
|
||||
if (matcher.find()) {
|
||||
ServerUtils.verbose("Unicode Filter: Caught Unicode: " + disallowedRegex);
|
||||
response.getReport().getStepsTaken().replace("Anti-Unicode", "`%s` %s".formatted(message, Emojis.alarm));
|
||||
|
||||
response.setBlocked(true);
|
||||
response.setPunished(Sentinel.mainConfig.chat.unicodeFilter.punished);
|
||||
response.setHighlightedMessage(Text.regexHighlighter(message,Sentinel.mainConfig.chat.unicodeFilter.regex," > ", " < "));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return event.getPlayer();
|
||||
}
|
||||
|
||||
public Report getReport() {
|
||||
return this.report;
|
||||
}
|
||||
|
||||
public boolean isPunished() {
|
||||
return punished;
|
||||
}
|
||||
|
||||
public AsyncChatEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(AsyncChatEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public void setReport(Report report) {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public String getOriginalMessage() {
|
||||
return originalMessage;
|
||||
}
|
||||
|
||||
public void setOriginalMessage(String originalMessage) {
|
||||
this.originalMessage = originalMessage;
|
||||
}
|
||||
|
||||
public String getHighlightedMessage() {
|
||||
return highlightedMessage;
|
||||
}
|
||||
|
||||
public void setHighlightedMessage(String highlightedMessage) {
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public void setPunished(boolean punished) {
|
||||
this.punished = punished;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.url;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.AbstractActionHandler;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import me.trouper.sentinel.utils.trees.HoverFormatter;
|
||||
import me.trouper.sentinel.utils.trees.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
|
||||
public class UrlAction extends AbstractActionHandler<UrlResponse> {
|
||||
@Override
|
||||
protected void punish(UrlResponse response) {
|
||||
for (String punishCommand : Sentinel.mainConfig.chat.urlFilter.punishCommands) {
|
||||
ServerUtils.sendCommand(punishCommand.replaceAll("%player%",response.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void staffWarning(UrlResponse response, Node tree) {
|
||||
String messageText = Text.prefix("&b&n%s&r &7%s".formatted(
|
||||
response.getPlayer().getName(),
|
||||
response.isPunished() ? Sentinel.lang.violations.chat.url.autoPunishNotification : Sentinel.lang.violations.chat.url.preventNotification
|
||||
));
|
||||
String hoverText = HoverFormatter.format(tree);
|
||||
|
||||
ServerUtils.forEachStaff(player -> player.sendMessage(Component.text(messageText).hoverEvent(Component.text(hoverText).asHoverEvent())));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playerWarning(UrlResponse response) {
|
||||
String message = Text.prefix(response.isPunished() ? Sentinel.lang.violations.chat.url.autoPunishWarning : Sentinel.lang.violations.chat.url.preventWarning);
|
||||
String hoverText = Sentinel.lang.automatedActions.actionAutomaticReportable;
|
||||
String command = "/sentinelcallback fpreport %s".formatted(response.getReport().getId());
|
||||
response.getPlayer().sendMessage(Component.text(message)
|
||||
.hoverEvent(Component.text(hoverText).asHoverEvent())
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND,command)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node buildTree(UrlResponse response) {
|
||||
Node root = new Node("Sentinel");
|
||||
root.addTextLine(Sentinel.lang.violations.chat.url.treeTitle);
|
||||
|
||||
Node playerInfo = new Node(Sentinel.lang.violations.chat.url.playerInfoTitle.formatted(response.getPlayer().getName()));
|
||||
playerInfo.addKeyValue(Sentinel.lang.violations.chat.url.uuid, response.getPlayer().getUniqueId().toString());
|
||||
root.addChild(playerInfo);
|
||||
|
||||
Node reportInfo = new Node(Sentinel.lang.violations.chat.url.reportInfoTitle);
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.url.originalMessage, response.getOriginalMessage());
|
||||
reportInfo.addField(Sentinel.lang.violations.chat.url.highlightedMessage, response.getHighlightedMessage());
|
||||
root.addChild(reportInfo);
|
||||
|
||||
Node actions = new Node(Sentinel.lang.violations.chat.url.actionTitle);
|
||||
actions.addTextLine(Sentinel.lang.violations.chat.url.blockAction);
|
||||
if (response.isPunished()) actions.addTextLine(Sentinel.lang.violations.chat.url.commandAction);
|
||||
root.addChild(actions);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldWarnPlayer(UrlResponse response) {
|
||||
return !Sentinel.mainConfig.chat.urlFilter.silent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.url;
|
||||
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
|
||||
public class UrlFilter {
|
||||
public static void handleUrlFilter(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Url Filter Opening: Event is canceled.");
|
||||
}
|
||||
|
||||
UrlResponse response = UrlResponse.generate(e);
|
||||
|
||||
if (response.isPunished() || response.isBlocked()) new UrlAction().run(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package me.trouper.sentinel.server.functions.chatfilter.url;
|
||||
|
||||
import io.github.retrooper.packetevents.adventure.serializer.legacy.LegacyComponentSerializer;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.Emojis;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FalsePositiveReporting;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.FilterResponse;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.Report;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.unicode.UnicodeResponse;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UrlResponse implements FilterResponse {
|
||||
private AsyncChatEvent event;
|
||||
private String originalMessage;
|
||||
private String highlightedMessage;
|
||||
private Report report;
|
||||
private boolean blocked;
|
||||
private boolean punished;
|
||||
|
||||
public UrlResponse(AsyncChatEvent event, String originalMessage, String highlightedMessage, Report report, boolean blocked, boolean punished) {
|
||||
this.event = event;
|
||||
this.report = report;
|
||||
this.originalMessage = originalMessage;
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
this.punished = punished;
|
||||
}
|
||||
|
||||
public static UrlResponse generate(AsyncChatEvent e) {
|
||||
if (e.isCancelled()) {
|
||||
ServerUtils.verbose("Unicode response opening: Event is canceled.");
|
||||
}
|
||||
|
||||
String message = LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
Report report = FalsePositiveReporting.initializeReport(message);
|
||||
UrlResponse response = new UrlResponse(e,message,message,report,false,false);
|
||||
for (String allowed : Sentinel.mainConfig.chat.urlFilter.whitelist) {
|
||||
message = message.replaceAll(allowed,"");
|
||||
}
|
||||
|
||||
response.getReport().getStepsTaken().put("Anti-URL","Removed allowed urls: %s".formatted(
|
||||
message
|
||||
));
|
||||
|
||||
String urlRegex = Sentinel.mainConfig.chat.urlFilter.regex;
|
||||
|
||||
Pattern pattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(message);
|
||||
|
||||
response.getReport().getStepsTaken().put("Anti-URL", "`%s`".formatted(
|
||||
message
|
||||
));
|
||||
|
||||
if (matcher.find()) {
|
||||
String highlighted = Text.regexHighlighter(message,Sentinel.mainConfig.chat.urlFilter.regex," > "," < ");
|
||||
ServerUtils.verbose("Caught URL: " + highlighted);
|
||||
response.getReport().getStepsTaken().replace("Anti-URL", "`%s` %s".formatted(highlighted, Emojis.alarm));
|
||||
|
||||
response.setBlocked(true);
|
||||
response.setPunished(Sentinel.mainConfig.chat.urlFilter.punished);
|
||||
response.setHighlightedMessage(highlighted);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return event.getPlayer();
|
||||
}
|
||||
|
||||
public Report getReport() {
|
||||
return this.report;
|
||||
}
|
||||
|
||||
public boolean isPunished() {
|
||||
return punished;
|
||||
}
|
||||
|
||||
public AsyncChatEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(AsyncChatEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public void setReport(Report report) {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public String getOriginalMessage() {
|
||||
return originalMessage;
|
||||
}
|
||||
|
||||
public void setOriginalMessage(String originalMessage) {
|
||||
this.originalMessage = originalMessage;
|
||||
}
|
||||
|
||||
public String getHighlightedMessage() {
|
||||
return highlightedMessage;
|
||||
}
|
||||
|
||||
public void setHighlightedMessage(String highlightedMessage) {
|
||||
this.highlightedMessage = highlightedMessage;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
public void setPunished(boolean punished) {
|
||||
this.punished = punished;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class Items {
|
||||
@@ -43,7 +42,7 @@ public class Items {
|
||||
.name(Text.color("&6&lSentinel &8&l|&f Anti-Nuke"))
|
||||
.lore(" ")
|
||||
.lore(Text.color("&bVersion&7: &f%s".formatted(Sentinel.getInstance().getDescription().getVersion())))
|
||||
.lore(Text.color("&bLicensed to&7: &f%s".formatted(Auth.getNonce())))
|
||||
.lore(Text.color("&bLicensed to&7: &f%s".formatted(Sentinel.getInstance().nonce)))
|
||||
.lore(" ")
|
||||
.lore(Text.color("&e&nAuthor(s)&r&e: &e%s").formatted(Sentinel.getInstance().getDescription().getAuthors()))
|
||||
.enchant(Enchantment.PROTECTION,64)
|
||||
|
||||
@@ -6,8 +6,9 @@ import me.trouper.sentinel.server.gui.ConfigGUI;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.ProfanityFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.RegexFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.SpamFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.UnicodeFilterGUI;
|
||||
import me.trouper.sentinel.server.gui.config.chat.UrlFilterGUI;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.Material;
|
||||
@@ -22,19 +23,23 @@ public class ChatGUI {
|
||||
.size(27)
|
||||
.onDefine(this::blankPage)
|
||||
.defineMain(this::mainClick)
|
||||
.define(11,REGEX_FILTERS,e->{
|
||||
ServerUtils.verbose("ChatGUI#home Redirecting to RegexFilterGUI");
|
||||
e.getWhoClicked().openInventory(new RegexFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(13,PROFANITY_FILTER,e->{
|
||||
e.getWhoClicked().openInventory(new ProfanityFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(15,SPAM_FILTER,e->{
|
||||
e.getWhoClicked().openInventory(new SpamFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(26,Items.BACK,e->{
|
||||
e.getWhoClicked().openInventory(new ConfigGUI().home.getInventory());
|
||||
})
|
||||
.define(16,PROFANITY_FILTER,e->{
|
||||
e.getWhoClicked().openInventory(new ProfanityFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(14,SPAM_FILTER,e->{
|
||||
e.getWhoClicked().openInventory(new SpamFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(12,URL_FILTER,e->{
|
||||
ServerUtils.verbose("URL Filter Launching");
|
||||
e.getWhoClicked().openInventory(new UrlFilterGUI().home.getInventory());
|
||||
})
|
||||
.define(10,UNICODE_FILTER,e->{
|
||||
ServerUtils.verbose("Unicode Filter Launching");
|
||||
e.getWhoClicked().openInventory(new UnicodeFilterGUI().home.getInventory());
|
||||
})
|
||||
.build();
|
||||
|
||||
private void mainClick(InventoryClickEvent e) {
|
||||
@@ -60,13 +65,15 @@ public class ChatGUI {
|
||||
.lore(Text.color("&8&l➥&7 Edit Heat Settings"))
|
||||
.build();
|
||||
|
||||
private static final ItemStack REGEX_FILTERS = ItemBuilder.create()
|
||||
.material(Material.DISPENSER)
|
||||
.name(Text.color("&bRegex Filters"))
|
||||
.lore(Text.color("&8&l➥&7 URL Blocker"))
|
||||
.lore(Text.color("&8&l➥&7 Unicode Whitelist"))
|
||||
.lore(Text.color("&8&l➥&7 Swear Regex"))
|
||||
.lore(Text.color("&8&l➥&7 Spam Regex"))
|
||||
.lore(Text.color("&8&l➥&7 False Positive Regex"))
|
||||
private static final ItemStack UNICODE_FILTER = ItemBuilder.create()
|
||||
.material(Material.PAPER)
|
||||
.name(Text.color("&bUnicode Filter"))
|
||||
.lore(Text.color("&8&l➥&7 Edit regex"))
|
||||
.build();
|
||||
|
||||
private static final ItemStack URL_FILTER = ItemBuilder.create()
|
||||
.material(Material.CHAIN)
|
||||
.name(Text.color("&bURL Filter"))
|
||||
.lore(Text.color("&8&l➥&7 Edit regex"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.chat;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -42,7 +44,7 @@ public class ProfanityFilterGUI {
|
||||
}
|
||||
ServerUtils.verbose("ProfanityFilterGUI#blankPage Page now blank");
|
||||
ItemStack top = Items.RED;
|
||||
if (Sentinel.mainConfig.chat.swearFilter.enabled) {
|
||||
if (Sentinel.mainConfig.chat.profanityFilter.enabled) {
|
||||
top = Items.GREEN;
|
||||
}
|
||||
|
||||
@@ -52,17 +54,18 @@ public class ProfanityFilterGUI {
|
||||
ServerUtils.verbose("ProfanityFilterGUI#blankPage Adding GUI Items");
|
||||
|
||||
inv.setItem(53,Items.BACK);
|
||||
inv.setItem(4,Items.booleanItem(Sentinel.mainConfig.chat.swearFilter.enabled, Items.configItem("Profanity Filter Toggle",Material.CLOCK,"Enable or Disable the whole Profanity filter")));
|
||||
inv.setItem(10,Items.intItem(Sentinel.mainConfig.chat.swearFilter.lowScore, Items.configItem("Low Score Gain", Material.WHITE_WOOL, "How much score will be added if the player \ndid not attempt to bypass the filter.")));
|
||||
inv.setItem(19,Items.intItem(Sentinel.mainConfig.chat.swearFilter.mediumLowScore, Items.configItem("Medium-Low Score Gain", Material.LIME_WOOL, "How much score will be added if the player \nused l33t speak to attempt a bypass")));
|
||||
inv.setItem(28,Items.intItem(Sentinel.mainConfig.chat.swearFilter.mediumScore, Items.configItem("Medium Score Gain", Material.YELLOW_WOOL, "How much score will be added if the player \nused sp/ecia|l characters to attempt a bypass")));
|
||||
inv.setItem(37,Items.intItem(Sentinel.mainConfig.chat.swearFilter.mediumHighScore, Items.configItem("Medium-High Score Gain", Material.ORANGE_WOOL, "How much score will be added if the player \nused reeeeeeepeating letters to attempt a bypass")));
|
||||
inv.setItem(46,Items.intItem(Sentinel.mainConfig.chat.swearFilter.highScore, Items.configItem("High Score Gain", Material.RED_WOOL, "How much score will be added if the player \nused pun. ctua, tion or spaces to attempt a bypass")));
|
||||
inv.setItem(29,Items.intItem(Sentinel.mainConfig.chat.swearFilter.regexScore, Items.configItem("Regex Score Gain", Material.DISPENSER, "How much score will be added if the player \nmatched the regex setting throughout \nthe processing of the message")));
|
||||
inv.setItem(22,Items.intItem(Sentinel.mainConfig.chat.swearFilter.punishScore, Items.configItem("Punish Score", Material.IRON_BARS, "If the player's score is above this \nthe punishment commands will be ran.")));
|
||||
inv.setItem(33,Items.intItem(Sentinel.mainConfig.chat.swearFilter.scoreDecay, Items.configItem("Score Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much score players will loose each minute.")));
|
||||
inv.setItem(31,Items.stringListItem(Sentinel.mainConfig.chat.swearFilter.swearPunishCommands,Material.WOODEN_AXE, "Default Punishment Commands", "%player% will be replaced with the offender's name"));
|
||||
inv.setItem(40,Items.stringListItem(Sentinel.mainConfig.chat.swearFilter.strictPunishCommands,Material.DIAMOND_AXE, "Strict Punishment Commands", "If words from the strict words list are flagged, \nthis list will be ran instead \n%player% will be replaced with the offender's name"));
|
||||
inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.profanityFilter.enabled, Items.configItem("Profanity Filter Toggle",Material.CLOCK,"Enable or Disable the whole Profanity filter")));
|
||||
inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.profanityFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing.")));
|
||||
inv.setItem(10,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.lowScore, Items.configItem("Low Score Gain", Material.WHITE_WOOL, "How much score will be added if the player \ndid not attempt to bypass the filter.")));
|
||||
inv.setItem(19,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumLowScore, Items.configItem("Medium-Low Score Gain", Material.LIME_WOOL, "How much score will be added if the player \nused l33t speak to attempt a bypass")));
|
||||
inv.setItem(28,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumScore, Items.configItem("Medium Score Gain", Material.YELLOW_WOOL, "How much score will be added if the player \nused sp/ecia|l characters to attempt a bypass")));
|
||||
inv.setItem(37,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.mediumHighScore, Items.configItem("Medium-High Score Gain", Material.ORANGE_WOOL, "How much score will be added if the player \nused reeeeeeepeating letters to attempt a bypass")));
|
||||
inv.setItem(46,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.highScore, Items.configItem("High Score Gain", Material.RED_WOOL, "How much score will be added if the player \nused pun. ctua, tion or spaces to attempt a bypass")));
|
||||
inv.setItem(29,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.regexScore, Items.configItem("Regex Score Gain", Material.DISPENSER, "How much score will be added if the player \nmatched the regex setting throughout \nthe processing of the message")));
|
||||
inv.setItem(22,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.punishScore, Items.configItem("Punish Score", Material.IRON_BARS, "If the player's score is above this \nthe punishment commands will be ran.")));
|
||||
inv.setItem(33,Items.intItem(Sentinel.mainConfig.chat.profanityFilter.scoreDecay, Items.configItem("Score Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much score players will loose each minute.")));
|
||||
inv.setItem(31,Items.stringListItem(Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands,Material.WOODEN_AXE, "Default Punishment Commands", "%player% will be replaced with the offender's name"));
|
||||
inv.setItem(40,Items.stringListItem(Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands,Material.DIAMOND_AXE, "Strict Punishment Commands", "If words from the strict words list are flagged, \nthis list will be ran instead \n%player% will be replaced with the offender's name"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -72,56 +75,61 @@ public class ProfanityFilterGUI {
|
||||
e.setCancelled(true);
|
||||
if (!MainGUI.verify((Player) e.getWhoClicked())) return;
|
||||
switch (e.getSlot()) {
|
||||
case 4 -> {
|
||||
Sentinel.mainConfig.chat.swearFilter.enabled = !Sentinel.mainConfig.chat.swearFilter.enabled;
|
||||
blankPage(e.getInventory());
|
||||
case 3 -> {
|
||||
Sentinel.mainConfig.chat.profanityFilter.enabled = !Sentinel.mainConfig.chat.profanityFilter.enabled;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.lowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.lowScore);
|
||||
case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.mediumLowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.mediumLowScore);
|
||||
case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.mediumScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.mediumScore);
|
||||
case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.mediumHighScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.mediumHighScore);
|
||||
case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.highScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.highScore);
|
||||
case 29 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.regexScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.regexScore);
|
||||
case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.punishScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.punishScore);
|
||||
case 33 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.swearFilter.scoreDecay = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.swearFilter.scoreDecay);
|
||||
case 5 -> {
|
||||
Sentinel.mainConfig.chat.profanityFilter.silent = !Sentinel.mainConfig.chat.profanityFilter.silent;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.lowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.lowScore);
|
||||
case 19 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumLowScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumLowScore);
|
||||
case 28 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumScore);
|
||||
case 37 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.mediumHighScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.mediumHighScore);
|
||||
case 46 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.highScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.highScore);
|
||||
case 29 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.regexScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.regexScore);
|
||||
case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.punishScore = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.punishScore);
|
||||
case 33 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.profanityFilter.scoreDecay = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.profanityFilter.scoreDecay);
|
||||
|
||||
case 31 -> {
|
||||
if (e.isLeftClick()) {
|
||||
queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> {
|
||||
cfg.chat.swearFilter.swearPunishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.swearFilter.swearPunishCommands);
|
||||
cfg.chat.profanityFilter.profanityPunishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands);
|
||||
return;
|
||||
}
|
||||
Sentinel.mainConfig.chat.swearFilter.swearPunishCommands.clear();
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.chat.profanityFilter.profanityPunishCommands.clear();
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
|
||||
}
|
||||
case 40 -> {
|
||||
if (e.isLeftClick()) {
|
||||
queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> {
|
||||
cfg.chat.swearFilter.strictPunishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.swearFilter.strictPunishCommands);
|
||||
cfg.chat.profanityFilter.strictPunishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands);
|
||||
return;
|
||||
}
|
||||
Sentinel.mainConfig.chat.swearFilter.strictPunishCommands.clear();
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.chat.profanityFilter.strictPunishCommands.clear();
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<MainConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package me.trouper.sentinel.server.gui.config.chat;
|
||||
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.SchedulerUtils;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.AdvancedConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.ChatGUI;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class RegexFilterGUI implements CustomListener {
|
||||
|
||||
public final CustomGui home = CustomGui.create()
|
||||
.title(Text.color("&6&lSentinel &8»&0 Edit a Chat Filter"))
|
||||
.size(27)
|
||||
.onDefine(this::blankPage)
|
||||
.defineMain(this::mainClick)
|
||||
.define(26, Items.BACK, e->{
|
||||
e.getWhoClicked().openInventory(new ChatGUI().home.getInventory());
|
||||
})
|
||||
.build();
|
||||
|
||||
private void blankPage(Inventory inv) {
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
inv.setItem(i,Items.BLANK);
|
||||
}
|
||||
|
||||
inv.setItem(26,Items.BACK);
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Setting up page");
|
||||
inv.setItem(11, Items.stringItem(Sentinel.advConfig.allowedCharRegex, Items.configItem("Unicode Whitelist", Material.FEATHER, "The regex defining what characters \nare allowed to be used in chat.")));
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Finished with Unicode Whitelist");
|
||||
inv.setItem(12, Items.stringItem(Sentinel.advConfig.falsePosRegex, Items.configItem("False Positive Regex", Material.EMERALD, "This regex will be replaced with \nan empty string before profanity filter \nprocessing begins.")));
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Finished with False Positive Regex");
|
||||
inv.setItem(13, Items.stringItem(Sentinel.advConfig.swearRegex, Items.configItem("Swear Regex", Material.ROTTEN_FLESH, "If anything matches to this regex, \nthe profanity filter will immediately flag it.")));
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Finished with Swear Regex");
|
||||
inv.setItem(14, Items.stringItem(Sentinel.advConfig.strictRegex, Items.configItem("Strict Regex", Material.LEAD, "If anything matches to this regex, the profanity \nfilter will immediately flag it as a slur.")));
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Finished with Strict Regex");
|
||||
inv.setItem(15, Items.stringItem(Sentinel.advConfig.urlRegex, Items.configItem("URL Blocker", Material.CHAIN, "If anything matches to this regex, it will get \nflagged as a URL.")));
|
||||
ServerUtils.verbose("RegexFilterGUI#blankPage Done Setting up page (Finished URL Blocker)");
|
||||
}
|
||||
|
||||
private void mainClick(InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
if (!MainGUI.verify((Player) e.getWhoClicked())) return;
|
||||
SchedulerUtils.later(0,()->{
|
||||
switch (e.getSlot()) {
|
||||
case 11 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.allowedCharRegex = args.getAll().toString(),Sentinel.advConfig.allowedCharRegex);
|
||||
case 12 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.falsePosRegex = args.getAll().toString(),Sentinel.advConfig.falsePosRegex);
|
||||
case 13 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.swearRegex = args.getAll().toString(),Sentinel.advConfig.swearRegex);
|
||||
case 14 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.strictRegex = args.getAll().toString(),Sentinel.advConfig.strictRegex);
|
||||
case 15 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.urlRegex = args.getAll().toString(),Sentinel.advConfig.urlRegex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, AdvancedConfig> updater = new ConfigUpdater<>(Sentinel.advConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<AdvancedConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
player.sendMessage(Text.prefix("Value updated successfully"));
|
||||
player.openInventory(home.getInventory());
|
||||
});
|
||||
player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.chat;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -50,7 +52,8 @@ public class SpamFilterGUI {
|
||||
}
|
||||
|
||||
inv.setItem(53,Items.BACK);
|
||||
inv.setItem(4,Items.booleanItem(Sentinel.mainConfig.chat.spamFilter.enabled, Items.configItem("Spam Filter Toggle", Material.CLOCK, "Enable or disable the whole Spam Filter")));
|
||||
inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.spamFilter.enabled, Items.configItem("Spam Filter Toggle", Material.CLOCK, "Enable or disable the whole Spam Filter")));
|
||||
inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.spamFilter.silent, Items.configItem("Silent Toggle", Material.FEATHER, "Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing.")));
|
||||
inv.setItem(10,Items.intItem(Sentinel.mainConfig.chat.spamFilter.defaultGain, Items.configItem("Default Heat Gain", Material.BUCKET, "How much heat will be added to each message.")));
|
||||
inv.setItem(19,Items.intItem(Sentinel.mainConfig.chat.spamFilter.lowGain, Items.configItem("Low Heat Gain", Material.WATER_BUCKET, "Extra heat to be added if the \nmessage is greater than 25% similar \nto their previous message.")));
|
||||
inv.setItem(28,Items.intItem(Sentinel.mainConfig.chat.spamFilter.mediumGain, Items.configItem("Medium Heat Gain", Material.COD_BUCKET, "Extra heat to be added if the \nmessage is greater than 50% similar \nto their previous message.")));
|
||||
@@ -60,6 +63,7 @@ public class SpamFilterGUI {
|
||||
inv.setItem(23,Items.intItem(Sentinel.mainConfig.chat.spamFilter.punishHeat, Items.configItem("Punish Heat", Material.IRON_BARS, "If the player's heat is above this \nthe punishment commands will be ran.")));
|
||||
inv.setItem(25,Items.intItem(Sentinel.mainConfig.chat.spamFilter.heatDecay, Items.configItem("Heat Decay", Material.DEAD_BUBBLE_CORAL_BLOCK, "How much heat players will loose each second.")));
|
||||
inv.setItem(32,Items.stringListItem(Sentinel.mainConfig.chat.spamFilter.punishCommands,Material.DIAMOND_AXE, "Punishment Commands", "%player% will be replaced with the offender's name"));
|
||||
inv.setItem(34,Items.stringListItem(Sentinel.mainConfig.chat.spamFilter.whitelist,Material.PAPER, "Message Whitelist", "Messages which will be ignored by the spam filter"));
|
||||
}
|
||||
|
||||
private void mainClick(InventoryClickEvent e) {
|
||||
@@ -68,10 +72,15 @@ public class SpamFilterGUI {
|
||||
blankPage(e.getInventory());
|
||||
|
||||
switch (e.getSlot()) {
|
||||
case 4 -> {
|
||||
case 3 -> {
|
||||
Sentinel.mainConfig.chat.spamFilter.enabled = !Sentinel.mainConfig.chat.spamFilter.enabled;
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
case 5 -> {
|
||||
Sentinel.mainConfig.chat.spamFilter.silent = !Sentinel.mainConfig.chat.spamFilter.silent;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 10 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.spamFilter.defaultGain = args.getAll().toInt(),"" + Sentinel.mainConfig.chat.spamFilter.defaultGain);
|
||||
@@ -97,15 +106,14 @@ public class SpamFilterGUI {
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<MainConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package me.trouper.sentinel.server.gui.config.chat;
|
||||
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.ChatGUI;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class UnicodeFilterGUI {
|
||||
|
||||
public final CustomGui home = CustomGui.create()
|
||||
.title(Text.color("&6&lSentinel &8»&0 Editing Unicode Filter"))
|
||||
.size(36)
|
||||
.onDefine(this::blankPage)
|
||||
.defineMain(this::mainClick)
|
||||
.define(35,Items.BACK, e->{
|
||||
e.getWhoClicked().openInventory(new ChatGUI().home.getInventory());
|
||||
})
|
||||
.build();
|
||||
|
||||
|
||||
private void blankPage(Inventory inv) {
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
inv.setItem(i, Items.BLANK);
|
||||
}
|
||||
ServerUtils.verbose("Unicode Filter GUI blank!");
|
||||
ItemStack top = Items.RED;
|
||||
if (Sentinel.mainConfig.chat.unicodeFilter.enabled) {
|
||||
top = Items.GREEN;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
inv.setItem(i,top);
|
||||
}
|
||||
|
||||
inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter")));
|
||||
inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing.")));
|
||||
inv.setItem(20,Items.booleanItem(Sentinel.mainConfig.chat.unicodeFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands.")));
|
||||
inv.setItem(22,Items.stringItem(Sentinel.mainConfig.chat.unicodeFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands.")));
|
||||
inv.setItem(24,Items.stringListItem(Sentinel.mainConfig.chat.unicodeFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled."));
|
||||
|
||||
}
|
||||
|
||||
private void mainClick(InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
if (!MainGUI.verify((Player) e.getWhoClicked())) return;
|
||||
|
||||
switch (e.getSlot()) {
|
||||
case 3 -> {
|
||||
Sentinel.mainConfig.chat.unicodeFilter.enabled = !Sentinel.mainConfig.chat.unicodeFilter.enabled;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 5 -> {
|
||||
Sentinel.mainConfig.chat.unicodeFilter.silent = !Sentinel.mainConfig.chat.unicodeFilter.silent;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 20 -> {
|
||||
Sentinel.mainConfig.chat.unicodeFilter.punished = !Sentinel.mainConfig.chat.unicodeFilter.punished;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 22 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.unicodeFilter.regex = args.getAll().toString(),Sentinel.mainConfig.chat.unicodeFilter.regex);
|
||||
|
||||
case 24 -> {
|
||||
if (e.isLeftClick()) {
|
||||
queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> {
|
||||
cfg.chat.unicodeFilter.punishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.unicodeFilter.punishCommands);
|
||||
return;
|
||||
}
|
||||
Sentinel.mainConfig.chat.unicodeFilter.punishCommands.clear();
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.save();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<MainConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
player.sendMessage(Text.prefix("Value updated successfully"));
|
||||
player.openInventory(home.getInventory());
|
||||
});
|
||||
player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package me.trouper.sentinel.server.gui.config.chat;
|
||||
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.events.CustomListener;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.ChatGUI;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class UrlFilterGUI implements CustomListener {
|
||||
public final CustomGui home = CustomGui.create()
|
||||
.title(Text.color("&6&lSentinel &8»&0 Editing Unicode Filter"))
|
||||
.size(36)
|
||||
.onDefine(this::blankPage)
|
||||
.defineMain(this::mainClick)
|
||||
.define(35, Items.BACK, e->{
|
||||
e.getWhoClicked().openInventory(new ChatGUI().home.getInventory());
|
||||
})
|
||||
.build();
|
||||
|
||||
|
||||
private void blankPage(Inventory inv) {
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
inv.setItem(i, Items.BLANK);
|
||||
}
|
||||
|
||||
ItemStack top = Items.RED;
|
||||
if (Sentinel.mainConfig.chat.urlFilter.enabled) {
|
||||
top = Items.GREEN;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
inv.setItem(i,top);
|
||||
}
|
||||
|
||||
inv.setItem(3,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.enabled, Items.configItem("Unicode Filter Toggle", Material.CLOCK,"Enable or Disable the whole Unicode filter")));
|
||||
inv.setItem(5,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.silent, Items.configItem("Silent Mode",Material.FEATHER,"Whether to notify players that their messages \nwere blocked. Enabling could help deter bypassing.")));
|
||||
inv.setItem(19,Items.booleanItem(Sentinel.mainConfig.chat.urlFilter.punished,Items.configItem("Punished",Material.IRON_BARS,"Toggles execution of punishment commands.")));
|
||||
inv.setItem(21,Items.stringItem(Sentinel.mainConfig.chat.urlFilter.regex,Items.configItem("Allowed Char Regex",Material.DISPENSER,"Toggles execution of punishment commands.")));
|
||||
inv.setItem(23,Items.stringListItem(Sentinel.mainConfig.chat.urlFilter.punishCommands,Material.DIAMOND_AXE,"Punishment Commands","Commands which will be executed if punishment is enabled."));
|
||||
inv.setItem(25,Items.stringListItem(Sentinel.mainConfig.chat.urlFilter.whitelist,Material.PAPER,"Whitelist","URLs which will not flag the filter."));
|
||||
|
||||
}
|
||||
|
||||
private void mainClick(InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
if (!MainGUI.verify((Player) e.getWhoClicked())) return;
|
||||
|
||||
switch (e.getSlot()) {
|
||||
case 3 -> {
|
||||
Sentinel.mainConfig.chat.urlFilter.enabled = !Sentinel.mainConfig.chat.urlFilter.enabled;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 5 -> {
|
||||
Sentinel.mainConfig.chat.urlFilter.silent = !Sentinel.mainConfig.chat.urlFilter.silent;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 19 -> {
|
||||
Sentinel.mainConfig.chat.urlFilter.punished = !Sentinel.mainConfig.chat.urlFilter.punished;
|
||||
Sentinel.mainConfig.save();
|
||||
blankPage(e.getInventory());
|
||||
}
|
||||
|
||||
case 21 -> queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> cfg.chat.urlFilter.regex = args.getAll().toString(),Sentinel.mainConfig.chat.urlFilter.regex);
|
||||
|
||||
case 23 -> {
|
||||
if (e.isLeftClick()) {
|
||||
queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> {
|
||||
cfg.chat.urlFilter.punishCommands.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.urlFilter.punishCommands);
|
||||
return;
|
||||
}
|
||||
Sentinel.mainConfig.chat.urlFilter.punishCommands.clear();
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.save();
|
||||
}
|
||||
|
||||
case 25 -> {
|
||||
if (e.isLeftClick()) {
|
||||
queuePlayer((Player) e.getWhoClicked(), (cfg,args) -> {
|
||||
cfg.chat.urlFilter.whitelist.add(args.getAll().toString());
|
||||
},"" + Sentinel.mainConfig.chat.urlFilter.whitelist);
|
||||
return;
|
||||
}
|
||||
Sentinel.mainConfig.chat.urlFilter.whitelist.clear();
|
||||
blankPage(e.getInventory());
|
||||
Sentinel.mainConfig.save();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncChatEvent, MainConfig> updater = new ConfigUpdater<>(Sentinel.mainConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<MainConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
player.sendMessage(Text.prefix("Value updated successfully"));
|
||||
player.openInventory(home.getInventory());
|
||||
});
|
||||
player.sendMessage(Component.text(Text.prefix("Enter the new value in chat. The value is currently set to &b%s&7. (Click to insert)".formatted(currentValue))).clickEvent(ClickEvent.suggestCommand(currentValue)));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -99,14 +101,14 @@ public class CBEditGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -99,14 +101,13 @@ public class CBMCPlaceGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -99,15 +101,14 @@ public class CBMCUseGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60,(e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -99,15 +101,14 @@ public class CBPlaceGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -99,15 +101,14 @@ public class CBUseGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -100,15 +102,14 @@ public class HotbarActionGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks.command;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -111,15 +113,14 @@ public class DangerousCMDGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks.command;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -83,14 +85,14 @@ public class LoggedCMDGUI {
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.trouper.sentinel.server.gui.config.nuke.checks.command;
|
||||
import io.github.itzispyder.pdk.commands.Args;
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.config.ConfigUpdater;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.ViolationConfig;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
@@ -12,6 +13,7 @@ import me.trouper.sentinel.utils.ServerUtils;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
@@ -88,15 +90,14 @@ public class SpecificCMDGUI {
|
||||
}
|
||||
|
||||
|
||||
public static ConfigUpdater<AsyncPlayerChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
public static ConfigUpdater<AsyncChatEvent, ViolationConfig> updater = new ConfigUpdater<>(Sentinel.violationConfig);
|
||||
|
||||
private void queuePlayer(Player player, BiConsumer<ViolationConfig, Args> action, String currentValue) {
|
||||
MainGUI.awaitingCallback.add(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
updater.queuePlayer(player, 20*60, (e)->{
|
||||
e.setCancelled(true);
|
||||
ServerUtils.verbose("Supplying the message: \"%s\". Canceled? %s".formatted(e.getMessage(),e.isCancelled()));
|
||||
return e.getMessage();
|
||||
return LegacyComponentSerializer.legacySection().serialize(e.message());
|
||||
}, (cfg, newValue) -> {
|
||||
action.accept(cfg,new Args(newValue.split("\\s+")));
|
||||
cfg.save();
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.CipherUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AntiPiracy {
|
||||
|
||||
public static boolean sailTheSevenSeas() {
|
||||
Sentinel.log.info("Docking Vesel");
|
||||
File sentinel = Sentinel.us;
|
||||
Sentinel.log.info("Obtaining Gold");
|
||||
String hash = CipherUtils.getFileHash(sentinel);
|
||||
Sentinel.log.info("Checking for fake coin");
|
||||
if (hash == null) {
|
||||
Sentinel.log.info("Counterfeiter!");
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<String> allowedHashes = getHashList();
|
||||
if (allowedHashes == null || allowedHashes.isEmpty()) {
|
||||
Sentinel.log.info("No Booty?");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allowedHashes.contains(hash)) {
|
||||
Sentinel.log.info("Checkpoint 1 Complete");
|
||||
return false;
|
||||
} else {
|
||||
Sentinel.log.info("Well,");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Set<String> getHashList() {
|
||||
Sentinel.log.info("Initializing East India Protocol");
|
||||
try {
|
||||
String urlString = "http://api.trouper.me:8080/sentinel-hashes";
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
Sentinel.log.info("Bargaining with merchants");
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException("Failed to get response from server, response code: " + responseCode);
|
||||
}
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuilder content = new StringBuilder();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
in.close();
|
||||
conn.disconnect();
|
||||
|
||||
Sentinel.log.info("Counting Coins");
|
||||
Gson gson = new Gson();
|
||||
|
||||
return gson.fromJson(content.toString(), new TypeToken<Set<String>>() {}.getType());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.AdvancedConfig;
|
||||
import me.trouper.sentinel.utils.CipherUtils;
|
||||
import me.trouper.sentinel.utils.MathUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -12,44 +14,20 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Auth {
|
||||
|
||||
public static String serverID = "NULL";
|
||||
public static String licenseKey = "NULL";
|
||||
public static String nonce = "NULL";
|
||||
public static String ip = "NULL";
|
||||
public static int port = 0;
|
||||
|
||||
public static boolean canLoad() {
|
||||
switch (authorize()) {
|
||||
case "AUTHORIZED" -> {
|
||||
return true;
|
||||
}
|
||||
case "MINEHUT" -> {
|
||||
boolean minehutStatus = Telemetry.initTelemetryHook() && Telemetry.report("Dynamic IP server has initialized.","Successful \"Auth\".");
|
||||
if (minehutStatus) {
|
||||
Sentinel.log.info("Dynamic IP auth Success!");
|
||||
return true;
|
||||
} else {
|
||||
Sentinel. log.info("Dynamic IP Failure. Webhook Error possible? Please contact obvWolf to fix this.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String authorize() {
|
||||
public static String authorize(String license, String identifier) {
|
||||
Map<String, List<String>> licenses = getLicenseList();
|
||||
if (licenses == null) return "ERROR";
|
||||
|
||||
if (licenses.containsKey(getLicenseKey())) {
|
||||
List<String> allowedIDs = licenses.get(getLicenseKey());
|
||||
if (allowedIDs.contains(serverID)) {
|
||||
if (licenses.containsKey(license)) {
|
||||
List<String> allowedIDs = licenses.getOrDefault(license,new ArrayList<>());
|
||||
if (allowedIDs.contains(identifier)) {
|
||||
return "AUTHORIZED";
|
||||
} else if (allowedIDs.contains("minehut")) {
|
||||
return "MINEHUT";
|
||||
@@ -61,16 +39,12 @@ public class Auth {
|
||||
return "UNREGISTERED";
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be the last auth variable called.
|
||||
* @return the unique identifier of the server
|
||||
*/
|
||||
public static String getServerID() {
|
||||
if (serverID == null || "NULL".equalsIgnoreCase(serverID)) {
|
||||
try {
|
||||
serverID = MathUtils.SHA256(getPublicIPAddress() + getPort());
|
||||
return serverID;
|
||||
} catch (Exception e) {
|
||||
return MathUtils.SHA256(getNonce() + getPort());
|
||||
}
|
||||
}
|
||||
return serverID;
|
||||
return CipherUtils.SHA256(Sentinel.getInstance().nonce + Sentinel.getInstance().ip + Sentinel.getInstance().port);
|
||||
}
|
||||
|
||||
public static Map<String, List<String>> getLicenseList() {
|
||||
@@ -95,7 +69,6 @@ public class Auth {
|
||||
conn.disconnect();
|
||||
|
||||
Gson gson = new Gson();
|
||||
|
||||
return gson.fromJson(content.toString(), new TypeToken<HashMap<String, List<String>>>() {}.getType());
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
@@ -103,51 +76,11 @@ public class Auth {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPublicIPAddress() throws IOException {
|
||||
String apiUrl = "http://checkip.amazonaws.com";
|
||||
|
||||
URL url = new URL(apiUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
try {
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String line;
|
||||
StringBuilder response = new StringBuilder();
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
|
||||
ip = response.toString().trim();
|
||||
|
||||
return ip;
|
||||
} else {
|
||||
throw new IOException("Failed to get public IP address. HTTP error code: " + responseCode);
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getPort() {
|
||||
port = Bukkit.getPort();
|
||||
return port;
|
||||
}
|
||||
|
||||
public static String getNonce() {
|
||||
if (nonce == null || nonce.equals("NULL")) nonce = MathUtils.MD5(AdvancedConfig.nonce);
|
||||
return nonce;
|
||||
return CipherUtils.MD5(AdvancedConfig.nonce);
|
||||
}
|
||||
|
||||
public static String getLicenseKey() {
|
||||
licenseKey = Sentinel.mainConfig.plugin.license;
|
||||
return licenseKey;
|
||||
return Sentinel.mainConfig.plugin.license;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.FileUtils;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BackdoorDetection {
|
||||
|
||||
public static void init() {
|
||||
Sentinel.log.info("Backdoor Detection Enabled, server is running on " + FileUtils.whoAmI());
|
||||
|
||||
Sentinel.log.info("Searching for patchfile at " + Vaccine.PATCH_FILE.getAbsolutePath());
|
||||
File folder = new File(Vaccine.PATCH_FILE.getAbsolutePath().replace("\\.patched","").replace("/.patched",""));
|
||||
|
||||
if (!Arrays.toString(folder.listFiles()).contains("\\.patched") && !Arrays.toString(folder.listFiles()).contains("/.patched")) {
|
||||
patchServerJar();
|
||||
makeSetupFile();
|
||||
} else {
|
||||
if (Vaccine.PATCH_FILE.delete()) {
|
||||
Sentinel.log.info("Patchfile verified successfully.");
|
||||
} else {
|
||||
Sentinel.log.info("Patchfile verified but not deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
if (Sentinel.mainConfig.backdoorDetection.setupMode && !Vaccine.SETUP_FILE.exists()) {
|
||||
makeSetupFile();
|
||||
}
|
||||
}
|
||||
|
||||
public static void patchServerJar() {
|
||||
File serverJar = new File(FileUtils.whoAmI());
|
||||
Sentinel.log.info("Creating a server jar with custom startup...");
|
||||
File tempJar = new File(serverJar.getPath() + "-patched");
|
||||
try {
|
||||
if (Injection.modifyJar(serverJar,Vaccine.class,tempJar)) {
|
||||
Sentinel.log.info("Successfully created a server jar with backdoor protection. It is located at %s. Replace your server jar with it to enable executable integrity checks.".formatted(tempJar.getAbsolutePath()));
|
||||
} else {
|
||||
Sentinel.log.info("Failed to patch your server jar.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Sentinel.log.info("Failed to patch your server jar.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeSetupFile() {
|
||||
Sentinel.log.info("Detected setup mode to be enabled in config, adding setup file.");
|
||||
|
||||
try {
|
||||
if (Vaccine.SETUP_FILE.getParentFile().mkdirs() && Vaccine.SETUP_FILE.createNewFile()) {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(Vaccine.SETUP_FILE));
|
||||
writer.write(Vaccine.PASSWORD);
|
||||
writer.flush();
|
||||
Sentinel.log.info("Successfully written to the file.");
|
||||
Sentinel.log.info("Jar file verification will be reset next server reboot. You are now free to add plugins.");
|
||||
if (Sentinel.mainConfig.backdoorDetection.keepSetupMode) return;
|
||||
Sentinel.mainConfig.backdoorDetection.setupMode = false;
|
||||
Sentinel.mainConfig.save();
|
||||
} else {
|
||||
Sentinel.log.info("Setup file already exists or could not be created.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error enabling setup mode.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
|
||||
public class IndirectLaunch {
|
||||
public static void launch() {
|
||||
Sentinel.getInstance().ip = ServerUtils.getPublicIPAddress();
|
||||
Sentinel.getInstance().port = ServerUtils.getPort();
|
||||
Sentinel.getInstance().nonce = Auth.getNonce();
|
||||
|
||||
Sentinel.log.info("Getting plugin file");
|
||||
|
||||
Sentinel.log.info("Reading Persistent files...");
|
||||
|
||||
Sentinel.getInstance().loadConfig();
|
||||
|
||||
Sentinel.log.info("Language Status: (%s)".formatted(Sentinel.lang.brokenLang));
|
||||
|
||||
Sentinel.log.info("Initializing Auth Identifier");
|
||||
|
||||
Sentinel.getInstance().identifier = Auth.getServerID();
|
||||
|
||||
Load.load(Sentinel.getInstance().license,Sentinel.getInstance().identifier, true);
|
||||
}
|
||||
}
|
||||
169
src/main/java/me/trouper/sentinel/startup/Injection.java
Normal file
169
src/main/java/me/trouper/sentinel/startup/Injection.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class Injection {
|
||||
|
||||
public static boolean modifyJar(File inputJar, Class<?> runnableClass, File outputJar) {
|
||||
// Read the JAR file's manifest
|
||||
try {
|
||||
JarFile jarFile = new JarFile(inputJar);
|
||||
Manifest manifest = jarFile.getManifest();
|
||||
|
||||
// Get the Main-Class from the manifest
|
||||
String mainClassName = manifest.getMainAttributes().getValue("Main-Class");
|
||||
|
||||
if (mainClassName == null) {
|
||||
throw new IllegalStateException("Main-Class attribute not found in the manifest.");
|
||||
}
|
||||
|
||||
// Prepare the output JAR and manifest
|
||||
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(outputJar), manifest);
|
||||
|
||||
// Add the Runnable class to the JAR (if it doesn't exist already)
|
||||
addRunnableClassToJar(jarFile, jarOut, runnableClass);
|
||||
|
||||
// Copy over the existing JAR entries (excluding the original Main-Class and duplicate classes)
|
||||
copyJarEntries(jarFile, jarOut, mainClassName);
|
||||
|
||||
// Modify the Main-Class's main method to call the Runnable
|
||||
modifyMainMethod(jarFile, jarOut, mainClassName, runnableClass.getName());
|
||||
|
||||
// Close the output stream
|
||||
jarOut.close();
|
||||
jarFile.close();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not patch your server jar! " + e);
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void addRunnableClassToJar(JarFile jarFile, JarOutputStream jarOut, Class<?> runnableClass) throws Exception {
|
||||
// Check if the Runnable class is already present in the JAR
|
||||
String runnableClassPath = runnableClass.getName().replace('.', '/') + ".class";
|
||||
if (isClassInJar(jarFile, runnableClassPath)) {
|
||||
System.out.println("Runnable class already exists in the JAR.");
|
||||
return; // Skip adding the class if it's already in the JAR
|
||||
}
|
||||
|
||||
// Convert the class to a byte array
|
||||
byte[] classBytes = getClassBytes(runnableClass);
|
||||
|
||||
// Create an entry for the Runnable class in the JAR
|
||||
JarEntry classEntry = new JarEntry(runnableClassPath);
|
||||
jarOut.putNextEntry(classEntry);
|
||||
|
||||
// Write the class byte array to the JAR
|
||||
jarOut.write(classBytes);
|
||||
jarOut.closeEntry();
|
||||
}
|
||||
|
||||
private static byte[] getClassBytes(Class<?> clazz) throws IOException {
|
||||
// Load the class file using ClassLoader and convert it to byte array
|
||||
InputStream inputStream = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
|
||||
if (inputStream == null) {
|
||||
throw new IOException("Class not found: " + clazz.getName());
|
||||
}
|
||||
|
||||
// Read the class file into byte array
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) > 0) {
|
||||
byteArrayOutputStream.write(buffer, 0, len);
|
||||
}
|
||||
inputStream.close();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
private static boolean isClassInJar(JarFile jarFile, String classPath) {
|
||||
// Check if the class already exists in the JAR
|
||||
JarEntry entry = jarFile.getJarEntry(classPath);
|
||||
return entry != null;
|
||||
}
|
||||
|
||||
private static void copyJarEntries(JarFile jarFile, JarOutputStream jarOut, String mainClassName) throws IOException {
|
||||
// Iterate over the entries in the JAR and copy them over to the output JAR
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (!entry.getName().equals("META-INF/MANIFEST.MF") && !entry.getName().equals(mainClassName.replace('.', '/') + ".class")) {
|
||||
// Skip manifest and main class file (to avoid duplication)
|
||||
jarOut.putNextEntry(entry);
|
||||
InputStream inputStream = jarFile.getInputStream(entry);
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) > 0) {
|
||||
jarOut.write(buffer, 0, len);
|
||||
}
|
||||
inputStream.close();
|
||||
jarOut.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void modifyMainMethod(JarFile jarFile, JarOutputStream jarOut, String mainClassName, String runnableClassName) throws IOException {
|
||||
// Modify the main method of the main class
|
||||
JarEntry entry = new JarEntry(mainClassName.replace('.', '/') + ".class");
|
||||
InputStream inputStream = jarFile.getInputStream(entry);
|
||||
|
||||
// Use ASM to read and modify the bytecode
|
||||
ClassReader classReader = new ClassReader(inputStream);
|
||||
ClassWriter classWriter = new ClassWriter(0);
|
||||
|
||||
ClassVisitor classVisitor = new MainMethodModifier(classWriter, runnableClassName);
|
||||
classReader.accept(classVisitor, 0);
|
||||
|
||||
// Write the modified class back to the JAR
|
||||
byte[] modifiedClass = classWriter.toByteArray();
|
||||
|
||||
jarOut.putNextEntry(entry);
|
||||
jarOut.write(modifiedClass);
|
||||
jarOut.closeEntry();
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
public static class MainMethodModifier extends ClassVisitor {
|
||||
private final String runnableClassName;
|
||||
|
||||
public MainMethodModifier(ClassWriter classWriter, String runnableClassName) {
|
||||
super(Opcodes.ASM9, classWriter);
|
||||
this.runnableClassName = runnableClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
// Check if the method is the main method (public static void main)
|
||||
if (name.equals("main") && descriptor.equals("([Ljava/lang/String;)V")) {
|
||||
// Modify the main method to add a call to the Runnable
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
return new MethodVisitor(Opcodes.ASM9, mv) {
|
||||
@Override
|
||||
public void visitCode() {
|
||||
super.visitCode();
|
||||
// Insert code to invoke the Runnable
|
||||
mv.visitTypeInsn(Opcodes.NEW, runnableClassName.replace('.', '/'));
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, runnableClassName.replace('.', '/'), "<init>", "()V", false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, runnableClassName.replace('.', '/'), "run", "()V", false);
|
||||
}
|
||||
};
|
||||
}
|
||||
// Return the original MethodVisitor for other methods
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@ package me.trouper.sentinel.startup;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.SchedulerUtils;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.AntiProfanity;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.AntiSpam;
|
||||
import me.trouper.sentinel.server.commands.*;
|
||||
import me.trouper.sentinel.server.events.*;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@@ -13,57 +13,54 @@ public class Load {
|
||||
|
||||
public static boolean lite = false;
|
||||
|
||||
public static void load(String license, String serverID) {
|
||||
/*if (AntiPiracy.sailTheSevenSeas()) {
|
||||
Sentinel.log.info("That's Aught to be the worst pirate I've ever seen...");
|
||||
Sentinel.log.warning("Your Sentinel distribution is illegal. Its DistroID is %s. It is not valid in the online registry.".formatted(CipherUtils.getFileHash(Sentinel.us)));
|
||||
liteStart("Failed Distribution Check (Modified Jar?)");
|
||||
return;
|
||||
}*/
|
||||
public static boolean load(String license, String identifier, boolean coldStart) {
|
||||
Sentinel.log.info("\n]====---- Requesting Authentication ----====[ \n- License Key: %s\n- Server ID: %s\n".formatted(license,identifier));
|
||||
try {
|
||||
Sentinel.log.info("Auth Requested...");
|
||||
switch (Auth.authorize()) {
|
||||
switch (Auth.authorize(license,identifier)) {
|
||||
case "AUTHORIZED" -> {
|
||||
Sentinel.log.info("\n]======----- Auth Success! -----======[");
|
||||
startup(true);
|
||||
startup(coldStart);
|
||||
return true;
|
||||
}
|
||||
case "MINEHUT" -> {
|
||||
boolean minehutStatus = Telemetry.initTelemetryHook() && Telemetry.report("Dynamic IP server has initialized.","Successful \"Auth\".");
|
||||
boolean minehutStatus = Telemetry.report("Dynamic IP server has authorized.","Success.");
|
||||
if (minehutStatus) {
|
||||
Sentinel.log.info("Dynamic IP auth Success!");
|
||||
startup(true);
|
||||
startup(coldStart);
|
||||
return true;
|
||||
} else {
|
||||
Sentinel. log.info("Dynamic IP Failure. Webhook Error possible? Please contact obvWolf to fix this.");
|
||||
liteStart("How is this even possible?");
|
||||
if (coldStart) liteStart("How is this even possible?");
|
||||
}
|
||||
}
|
||||
case "INVALID-ID" -> {
|
||||
Sentinel.log.info("Authentication Failure, You have not whitelisted this server ID yet.");
|
||||
liteStart("They have not whitelisted their server ID yet. (License exists, no ID)");
|
||||
if (coldStart) liteStart("They have not whitelisted their server ID yet. (License exists, no ID)");
|
||||
}
|
||||
case "UNREGISTERED" -> {
|
||||
Sentinel.log.warning("Authentication Failure, YOU SHALL NOT PASS! License: %s Server ID: %s".formatted(license,serverID));
|
||||
liteStart("They do not have a license key");
|
||||
Sentinel.log.warning("Authentication Failure, YOU SHALL NOT PASS! License: %s Server ID: %s".formatted(license,identifier));
|
||||
if (coldStart) liteStart("They do not have a license key");
|
||||
}
|
||||
case "ERROR" -> {
|
||||
Sentinel.log.warning("Hmmmmmm thats not right... License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,serverID));
|
||||
liteStart("An expected error occurred which prevented them from launching");
|
||||
Sentinel.log.warning("Hmmmmmm thats not right... License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier));
|
||||
if (coldStart) liteStart("An expected error occurred which prevented them from launching");
|
||||
}
|
||||
default -> {
|
||||
Sentinel.log.warning("Achievement unlocked: How did we get here? License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,serverID));
|
||||
liteStart("An unexpected error occured which prevented them from launching");
|
||||
Sentinel.log.warning("Achievement unlocked: How did we get here? License: %s Server ID: %s\nPlease report the above stacktrace.".formatted(license,identifier));
|
||||
if (coldStart) liteStart("An unexpected error occured which prevented them from launching");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Sentinel.log.info("WTFFFF ARE YOU DOING MAN??????");
|
||||
liteStart("An exception was thrown, then caught.");
|
||||
if (coldStart) liteStart("An exception was thrown, then caught.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void liteStart(String reason) {
|
||||
lite = true;
|
||||
Telemetry.initTelemetryHook();
|
||||
Telemetry.report("Server has launched in lite mode",reason);
|
||||
|
||||
new SentinelCommand().register();
|
||||
@@ -81,7 +78,7 @@ public class Load {
|
||||
""");
|
||||
|
||||
|
||||
SchedulerUtils.repeat(20*30,()->{
|
||||
SchedulerUtils.repeat(20*60,()->{
|
||||
if (lite) {
|
||||
Sentinel.log.info(Text.removeColors(SentinelCommand.liteMode));
|
||||
}
|
||||
@@ -113,14 +110,14 @@ public class Load {
|
||||
new CommandExecuteEvent().register();
|
||||
new CreativeHotbarEvent().register();
|
||||
new TrapCommand().register();
|
||||
new PluginCloakingEvents().register();
|
||||
if (Sentinel.doNoPlugins) {
|
||||
PluginCloakingEvents.registerEvent(Sentinel.getInstance());
|
||||
}
|
||||
new PluginCloakingEvent().register();
|
||||
|
||||
// Scheduled timers
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), AntiSpam::decayHeat,0, 20);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), AntiProfanity::decayScore,0,1200);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), SpamFilter::decayHeat,0, 20);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200);
|
||||
|
||||
if (Sentinel.mainConfig.backdoorDetection.enabled) BackdoorDetection.init();
|
||||
|
||||
Sentinel.log.info("""
|
||||
Finished!
|
||||
____ __ ___ \s
|
||||
|
||||
@@ -1,60 +1,65 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
|
||||
import io.github.itzispyder.pdk.utils.misc.Timer;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.MainConfig;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
import me.trouper.sentinel.utils.CipherUtils;
|
||||
import me.trouper.sentinel.utils.trees.EmbedFormatter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Telemetry {
|
||||
|
||||
public static String webhook;
|
||||
|
||||
public static boolean initTelemetryHook() {
|
||||
webhook = fetchTelemetryHook();
|
||||
return !webhook.equals("NULL");
|
||||
}
|
||||
private static String webhook;
|
||||
private static final String API_URL = "http://api.trouper.me:8080/api/webhook";
|
||||
private static final String API_TOKEN = "this-really-isnt-needed";
|
||||
|
||||
public static boolean report(String title, String reason) {
|
||||
int port = Auth.getPort();
|
||||
DiscordEmbed embed = new DiscordEmbed.Builder()
|
||||
.color(0xFF0000)
|
||||
.author("Sentinel Startup Log")
|
||||
.title(title)
|
||||
.desc("""
|
||||
**__License Info__**
|
||||
License: `%s`
|
||||
Nonce: `%s`
|
||||
File Hash: `%s`
|
||||
Purchaser: `%s` | `%s`
|
||||
|
||||
**__Server Info__**
|
||||
ID: `%s`
|
||||
Address: `%s`
|
||||
Port: `%s`
|
||||
|
||||
**__Extra Info__**
|
||||
%s
|
||||
""".formatted(
|
||||
Auth.getLicenseKey(),
|
||||
Auth.getNonce(),
|
||||
CipherUtils.getFileHash(Sentinel.us),
|
||||
MainConfig.username,
|
||||
MainConfig.user,
|
||||
Auth.getServerID(),
|
||||
Auth.ip,
|
||||
port,
|
||||
reason
|
||||
)).build();
|
||||
|
||||
try {
|
||||
if (webhook == null || webhook.isBlank()) webhook = fetchTelemetryHook();
|
||||
|
||||
DiscordEmbed embed = new DiscordEmbed.Builder()
|
||||
.color(0xFF0000)
|
||||
.author("Sentinel Startup Log")
|
||||
.title(title)
|
||||
.desc("""
|
||||
**__License Info__**
|
||||
License: `%s`
|
||||
Nonce: `%s`
|
||||
Purchaser: `%s` | `%s`
|
||||
|
||||
**__Server Info__**
|
||||
ID: `%s`
|
||||
Address: `%s`
|
||||
Port: `%s`
|
||||
|
||||
**__Extra Info__**
|
||||
%s
|
||||
""".formatted(
|
||||
Sentinel.getInstance().license,
|
||||
Sentinel.getInstance().nonce,
|
||||
MainConfig.username,
|
||||
MainConfig.user,
|
||||
Sentinel.getInstance().identifier,
|
||||
Sentinel.getInstance().ip,
|
||||
Sentinel.getInstance().port,
|
||||
reason
|
||||
)).build();
|
||||
|
||||
|
||||
EmbedFormatter.sendEmbed(embed,webhook);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
@@ -63,62 +68,24 @@ public class Telemetry {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String fetchTelemetryHook() {
|
||||
if (webhook == null || "NULL".equals(webhook)) {
|
||||
try {
|
||||
final String webhook = extractWebhook(fetchHtmlContent("https://trouper.me/auth/telemetry"));
|
||||
// ServerUtils.verbose("Original Webhook: " + webhook);
|
||||
public static String fetchTelemetryHook() throws Exception {
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(10))
|
||||
.build();
|
||||
|
||||
String webhookIdPart = webhook.replaceAll(".*/(\\d+)/([^/]+.*)$", "/$1/");
|
||||
// ServerUtils.verbose("Webhook ID Part: " + webhookIdPart);
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(API_URL))
|
||||
.header("Authorization", API_TOKEN)
|
||||
.timeout(Duration.ofSeconds(10))
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
String encrypted = webhook.replaceAll(".*/\\d+/([^/]+.*)$", "$1");
|
||||
// ServerUtils.verbose("Encrypted Part: " + encrypted);
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
String isolated = webhook.replaceAll("/\\d+/([^/]+.*)$", "");
|
||||
//ServerUtils.verbose("Isolated Part: " + isolated);
|
||||
|
||||
String decrypted = isolated + webhookIdPart + CipherUtils.decrypt(encrypted);
|
||||
//ServerUtils.verbose("Decrypted Result: " + decrypted);
|
||||
|
||||
return decrypted;
|
||||
} catch (Exception ex) {
|
||||
Sentinel.log.warning("FAILED TO LOAD TELEMETRY (Are the servers up?)");
|
||||
ex.printStackTrace();
|
||||
return "NULL";
|
||||
}
|
||||
}
|
||||
return webhook;
|
||||
}
|
||||
|
||||
private static String fetchHtmlContent(String urlString) throws Exception {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
connection.disconnect();
|
||||
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
private static String extractWebhook(String htmlContent) {
|
||||
String pattern = "data-hook=\"(.*?)\"";
|
||||
Pattern r = Pattern.compile(pattern);
|
||||
Matcher m = r.matcher(htmlContent);
|
||||
|
||||
if (m.find()) {
|
||||
return m.group(1);
|
||||
if (response.statusCode() == 200) {
|
||||
return response.body();
|
||||
} else {
|
||||
return "Webhook ID not found";
|
||||
throw new TimeoutException("Failed to get webhook (Status Code): " + response.statusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
290
src/main/java/me/trouper/sentinel/startup/Vaccine.java
Normal file
290
src/main/java/me/trouper/sentinel/startup/Vaccine.java
Normal file
@@ -0,0 +1,290 @@
|
||||
package me.trouper.sentinel.startup;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.*;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Vaccine implements Runnable {
|
||||
/*
|
||||
Below is a discussion of several flaws in the design and implementation of this “Vaccine” class. In short, while the idea is to “lock down” the server’s jar files, there are several ways an attacker (or even an inadvertent change) could cause the protection to fail or be bypassed. Here are some of the main issues:
|
||||
|
||||
---
|
||||
|
||||
### 1. Use of MD5 for Integrity Checking
|
||||
|
||||
- **Weak Hash Algorithm:**
|
||||
The code uses MD5 to calculate file checksums. MD5 is now considered cryptographically broken and subject to collision attacks. An attacker with the skills to create a jar file that collides with a legitimate MD5 hash might be able to bypass the integrity check.
|
||||
|
||||
- **Collision Attacks:**
|
||||
Since MD5 is not collision resistant, it may be possible to generate two different jar files that yield the same MD5 hash. This makes the “fingerprint” of a jar file untrustworthy.
|
||||
|
||||
---
|
||||
|
||||
### 2. Hard-Coded Password and Key Management
|
||||
|
||||
- **Static Password:**
|
||||
The password `"SentinelAntiNuke"` is hard-coded. An attacker who reverse engineers your plugin or reads the source (or decompiles the bytecode) will know the “secret” used for both the setup file check and for encrypting/decrypting the checksum file.
|
||||
|
||||
- **Setup Mode Abuse:**
|
||||
The existence of a “setup” file (with the same hard-coded password inside) causes the plugin to clear the existing checksums and re-register all jar files. An attacker who can write to the server directory could create such a file (or modify it) and force the plugin to re-register even malicious files.
|
||||
|
||||
---
|
||||
|
||||
### 3. Insecure Encryption Practices
|
||||
|
||||
- **AES in ECB Mode:**
|
||||
In your `getCipher` method you call `Cipher.getInstance("AES")`, which in many Java versions defaults to `"AES/ECB/PKCS5Padding"`. ECB (Electronic Codebook) mode is not semantically secure – it does not use an IV, and identical blocks of plaintext result in identical ciphertext. While you are not encrypting extremely sensitive data here, it is still not a best practice.
|
||||
|
||||
- **Key Derivation:**
|
||||
The key is derived by taking the SHA‑256 hash of the password and using the first 16 bytes. This is not as robust as using a proper key derivation function (like PBKDF2) with a salt and many iterations. Although the password is fixed, this makes it trivial for an attacker to derive the same key.
|
||||
|
||||
- **Checksum File Tampering:**
|
||||
The checksum file (`.checksums.lock`) is encrypted with a key that is easily re-derived from the hard-coded password. An attacker who can access the filesystem can decrypt, modify, and re-encrypt this file to “legitimize” malicious jar files.
|
||||
|
||||
---
|
||||
|
||||
### 4. Mapping by File Name Only
|
||||
|
||||
- **Insufficient Uniqueness:**
|
||||
The plugin uses the jar’s filename (using `file.getName()`) as the key in the checksum map. This means that if there are two jar files with the same name in different directories, they will collide in the map. An attacker might take advantage of this by placing a malicious jar in a different directory under a known-good name.
|
||||
|
||||
- **Case Sensitivity Issues:**
|
||||
The code checks for files ending in `".jar"` (a case‑sensitive match). A file named `"plugin.JAR"` (with different casing) would be skipped by the integrity check.
|
||||
|
||||
---
|
||||
|
||||
### 5. Deserialization of Encrypted Data
|
||||
|
||||
- **Use of Object Serialization:**
|
||||
The checksum file is stored as a serialized Java object. Even though it is encrypted, once an attacker can compute the key (which they easily can due to the hard-coded password), they might be able to craft malicious serialized data. In Java, deserialization of untrusted data can lead to remote code execution if there is a gadget chain available.
|
||||
|
||||
---
|
||||
|
||||
### 6. File Operation Issues
|
||||
|
||||
- **Renaming for Quarantine:**
|
||||
When a jar file fails the checksum test, the plugin attempts to “quarantine” it by renaming it with a `.quarantined` suffix. However, the code does not check whether the rename operation succeeded. If it fails, the jar might still be loaded.
|
||||
|
||||
- **Recursive Directory Traversal:**
|
||||
The recursion into subdirectories does not protect against cycles (e.g. via symbolic links). Although this is more of a robustness/usability concern than a direct security issue, it could be exploited in some situations.
|
||||
|
||||
---
|
||||
|
||||
### 7. General Trust Model Problems
|
||||
|
||||
- **Reliance on the Filesystem:**
|
||||
The entire scheme is based on file checksums stored on disk. If an attacker already has the ability to modify files on the server, they can simply change the checksum file (or the jar files and then re-run the setup process) to bypass your integrity checks.
|
||||
|
||||
- **No External Trust Anchor:**
|
||||
There is no digital signature or external verification. Everything depends on locally stored values that, once known, are trivial for an attacker to forge.
|
||||
|
||||
---
|
||||
|
||||
### **Summary**
|
||||
|
||||
While the intent of “injecting” code to check MD5 sums of jar files might seem to raise the bar against backdoors, the design is fundamentally flawed:
|
||||
|
||||
- **MD5 is too weak** to trust for integrity checking.
|
||||
- **A hard-coded password** means that the “secret” is public to anyone who looks at the code.
|
||||
- **The encryption method (AES in ECB mode, with a trivial key derivation)** is not sufficient to protect the checksum file from tampering.
|
||||
- **Mapping by just filename** (with case‑sensitivity issues) opens up additional attack vectors.
|
||||
- **The re‑registration process** (triggered by a setup file) can be abused by an attacker with filesystem access.
|
||||
|
||||
Any attacker who has access to the server’s filesystem (or who can upload files) can defeat these measures by simply regenerating the checksums after introducing malicious code.
|
||||
|
||||
---
|
||||
|
||||
### **Recommendations**
|
||||
|
||||
- **Use a stronger hash:** Replace MD5 with SHA‑256 (or better) for file integrity.
|
||||
- **Improve key management:** Avoid hard-coding passwords; consider using a secure configuration that isn’t embedded in code.
|
||||
- **Use a secure cipher mode:** Switch from ECB to a mode such as CBC or GCM and use a proper IV.
|
||||
- **Include full paths:** When mapping files, use their canonical paths (or a similar unique identifier) rather than just the filename.
|
||||
- **Avoid unsafe deserialization:** If you must persist data, use a safer data format (such as JSON or XML) and validate it.
|
||||
- **Add a digital signature:** Instead of (or in addition to) simple checksums, sign your jar files with a trusted certificate.
|
||||
|
||||
By addressing these issues, you would significantly improve the robustness of your server’s integrity checking mechanism.
|
||||
*/
|
||||
|
||||
public static final String PASSWORD = "%%__TIMESTAMP__%%"; // This can be replaced with a dynamic password input method
|
||||
private static final File FOLDER = new File(".sentinel/");
|
||||
private static final File CHECKSUM_FILE = new File(FOLDER, ".checksums.lock");
|
||||
public static final File SETUP_FILE = new File(FOLDER, ".checksums.setup"); // Setup file to reset checksums
|
||||
public static final File PATCH_FILE = new File(FOLDER, ".patched"); // PatchFile name
|
||||
|
||||
private static Map<File, String> fileChecksums = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("This server is protected by Sentinel Anti-Nuke.");
|
||||
if (!FOLDER.exists() && !FOLDER.mkdirs()) {
|
||||
System.out.println("Failed to make directories.");
|
||||
System.exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (PATCH_FILE.exists() || PATCH_FILE.createNewFile()) {
|
||||
System.out.println("Patchfile verified successfully.");
|
||||
} else {
|
||||
System.out.println("Unable to verify patch file. Sentinel may re-inject me! If you see this message twice in one startup, delete your server jar and try again.");
|
||||
}
|
||||
|
||||
File dir = new File(System.getProperty("user.dir"));
|
||||
// Check if the setup file exists and validate the password
|
||||
if (loadChecksums()) {
|
||||
System.out.println("Successfully loaded checksums.");
|
||||
} else {
|
||||
System.out.println("This error should only occur on first startup of the custom server jar.");
|
||||
}
|
||||
if ((SETUP_FILE.exists())) {
|
||||
if (validatePassword(SETUP_FILE)) {
|
||||
System.out.println("Entering setup mode.");
|
||||
clearChecksums();
|
||||
System.out.println("Registering executable checksums.");
|
||||
registerFiles(dir);
|
||||
if (!SETUP_FILE.delete()) {
|
||||
System.out.println("Setup finished. Mode exited.");
|
||||
} else {
|
||||
System.out.println("Setup finished. Could not exit setup mode, please delete \".sentinel/.checksums.setup\" manually.");
|
||||
}
|
||||
} else {
|
||||
System.out.println("Invalid password in setup file.");
|
||||
}
|
||||
} else {
|
||||
System.out.println("Setup file not found.");
|
||||
}
|
||||
|
||||
// Verify the integrity of .jar files
|
||||
System.out.println("Verifying executable integrity.");
|
||||
verifyJarFiles(dir);
|
||||
|
||||
// Save checksums to encrypted file
|
||||
saveChecksums();
|
||||
System.out.println("Saved checksums.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validates the password in the .checksums.setup file
|
||||
private static boolean validatePassword(File setupFile) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(setupFile))) {
|
||||
String storedPassword = reader.readLine();
|
||||
return storedPassword != null && storedPassword.equals(PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the checksum map
|
||||
private static void clearChecksums() {
|
||||
fileChecksums.clear();
|
||||
}
|
||||
|
||||
// Registers all .jar files in the directory into the checksum map
|
||||
private static void registerFiles(File dir) throws Exception {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
// Recursively call the method on subdirectories
|
||||
registerFiles(file.getAbsoluteFile());
|
||||
} else if (file.getName().toLowerCase().endsWith(".jar")) {
|
||||
String md5 = calculateMD5(file);
|
||||
System.out.printf("%s -> %s%n", file.getAbsolutePath(), md5);
|
||||
fileChecksums.put(file, md5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the integrity of .jar files by checking their MD5 checksum
|
||||
private static void verifyJarFiles(File dir) throws Exception {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
// Recursively call the method on subdirectories
|
||||
verifyJarFiles(file);
|
||||
} else if (file.getName().toLowerCase().endsWith(".jar")) {
|
||||
String storedMD5 = fileChecksums.get(file.getAbsoluteFile());
|
||||
String currentMD5 = calculateMD5(file);
|
||||
System.out.printf("Checking %s, Sum: %s%n", file.getAbsoluteFile(), currentMD5);
|
||||
if (storedMD5 == null || !storedMD5.equals(currentMD5)) {
|
||||
// If the MD5 doesn't match or the file is not registered, quarantine it
|
||||
System.out.printf("%s has an invalid checksum. It has been quarantined.%n", file.getName());
|
||||
quarantineFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Quarantines the file by renaming it with .quarantined suffix
|
||||
private static void quarantineFile(File file) {
|
||||
File quarantinedFile = new File(file.getParent(), file.getName() + ".quarantined");
|
||||
file.renameTo(quarantinedFile);
|
||||
}
|
||||
|
||||
// Calculates the MD5 hash of a file
|
||||
private static String calculateMD5(File file) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = is.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
byte[] hashBytes = md.digest();
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hashBytes) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
// Saves the current file checksums to an encrypted file
|
||||
private static void saveChecksums() throws Exception {
|
||||
try (ObjectOutputStream oos = new ObjectOutputStream(new CipherOutputStream(
|
||||
new FileOutputStream(CHECKSUM_FILE),
|
||||
getCipher(Cipher.ENCRYPT_MODE)))) {
|
||||
oos.writeObject(fileChecksums);
|
||||
}
|
||||
}
|
||||
|
||||
// Loads checksums from the encrypted .checksums.lock file
|
||||
private static boolean loadChecksums() {
|
||||
try (ObjectInputStream ois = new ObjectInputStream(new CipherInputStream(
|
||||
new FileInputStream(CHECKSUM_FILE),
|
||||
getCipher(Cipher.DECRYPT_MODE)))) {
|
||||
fileChecksums = (Map<File, String>) ois.readObject();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a Cipher instance for the specified mode (encryption or decryption)
|
||||
private static Cipher getCipher(int mode) throws Exception {
|
||||
// Generate a hashed key from the password using SHA-256
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hashedPassword = digest.digest(PASSWORD.getBytes());
|
||||
|
||||
// Create AES key from the hashed password
|
||||
SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(hashedPassword, 16), "AES"); // Using the first 16 bytes of the SHA-256 hash
|
||||
Cipher cipher = Cipher.getInstance("AES");
|
||||
cipher.init(mode, key);
|
||||
return cipher;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
@@ -72,4 +73,52 @@ public class CipherUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String SHA256(String input) {
|
||||
try {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
|
||||
byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
|
||||
for (byte b : encodedHash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String MD5(String input) {
|
||||
try {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
|
||||
|
||||
byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
|
||||
for (byte b : encodedHash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,30 @@ import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static String whoAmI() {
|
||||
String classPath = System.getProperty("java.class.path");
|
||||
String[] parts = classPath.split(File.pathSeparator);
|
||||
for (String part : parts) {
|
||||
if (part.endsWith(".jar")) {
|
||||
return new File(part).getName();
|
||||
}
|
||||
}
|
||||
return "Unknown.jar";
|
||||
}
|
||||
|
||||
public static boolean fileExists(File file) {
|
||||
try {
|
||||
if (!file.getParentFile().exists()) {
|
||||
return false;
|
||||
} else {
|
||||
return file.exists();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean folderExists(String folderName) {
|
||||
File folder = new File(Sentinel.dataFolder(), folderName);
|
||||
return folder.exists() && folder.isDirectory();
|
||||
|
||||
@@ -130,7 +130,7 @@ public class ItemUtils {
|
||||
} else if (enchantment.equals(Enchantment.UNBREAKING)) {
|
||||
maxLevel = Sentinel.nbtConfig.maxUnbreaking;
|
||||
} else if (enchantment.equals(Enchantment.VANISHING_CURSE)) {
|
||||
maxLevel = Sentinel.nbtConfig.maxVanishing;
|
||||
maxLevel = Sentinel.nbtConfig.maxCurseOfVanishing;
|
||||
} else if (enchantment.equals(Enchantment.BINDING_CURSE)) {
|
||||
maxLevel = Sentinel.nbtConfig.maxCurseOfBinding;
|
||||
} else if (enchantment.equals(Enchantment.AQUA_AFFINITY)) {
|
||||
|
||||
@@ -32,54 +32,6 @@ public class MathUtils {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String SHA256(String input) {
|
||||
try {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
|
||||
byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
|
||||
for (byte b : encodedHash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String MD5(String input) {
|
||||
try {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
|
||||
|
||||
byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
|
||||
for (byte b : encodedHash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static double calcSim(String s1, String s2) {
|
||||
int maxLength = Math.max(s1.length(), s2.length());
|
||||
if (maxLength == 0) {
|
||||
|
||||
@@ -8,6 +8,11 @@ import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -102,4 +107,41 @@ public class ServerUtils {
|
||||
public static String[] unVanishedPlayers() {
|
||||
return io.github.itzispyder.pdk.utils.ServerUtils.players(ServerUtils::isVanished).stream().map(Player::getName).toArray(String[]::new);
|
||||
}
|
||||
|
||||
public static String getPublicIPAddress() {
|
||||
try {
|
||||
String apiUrl = "http://checkip.amazonaws.com";
|
||||
|
||||
URL url = new URL(apiUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String line;
|
||||
StringBuilder response = new StringBuilder();
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
|
||||
return response.toString().trim();
|
||||
}
|
||||
connection.disconnect();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Sentinel.log.warning(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getPort() {
|
||||
return Bukkit.getPort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.regex.PatternSyntaxException;
|
||||
public class Text {
|
||||
|
||||
public static String removeColors(String input) {
|
||||
return input.replaceAll("§[0-9a-fr]", "");
|
||||
return input.replaceAll("§[0-9a-frn]", "");
|
||||
}
|
||||
|
||||
public static String regexHighlighter(String input, String regex, String startString, String endString) {
|
||||
|
||||
Reference in New Issue
Block a user