From 97d395a750c1ca6f09010523a331fae3f900d59a Mon Sep 17 00:00:00 2001 From: trouper Date: Sun, 3 Sep 2023 19:58:31 -0500 Subject: [PATCH] Added Minehut (Dynamic IP) auth and redid config --- gradle.properties | 2 +- .../github/thetrouper/sentinel/Sentinel.java | 119 +++++++------ .../thetrouper/sentinel/data/Config.java | 22 ++- .../thetrouper/sentinel/events/ChatEvent.java | 21 ++- .../server/functions/Authenticator.java | 8 +- .../sentinel/server/functions/Message.java | 14 ++ .../server/functions/ProfanityFilter.java | 3 +- .../sentinel/server/functions/Telemetry.java | 156 ++++++++++++++++++ .../sentinel/server/util/FileUtils.java | 8 +- 9 files changed, 287 insertions(+), 66 deletions(-) create mode 100644 src/main/java/io/github/thetrouper/sentinel/server/functions/Telemetry.java diff --git a/gradle.properties b/gradle.properties index c05579f..57fa22c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Plugin group = 'io.github.thetrouper' -version = 0.1.8 +version = 0.1.9 # Minecraft mc_version = 1.19.4 diff --git a/src/main/java/io/github/thetrouper/sentinel/Sentinel.java b/src/main/java/io/github/thetrouper/sentinel/Sentinel.java index 3b9ba22..b3b0a09 100644 --- a/src/main/java/io/github/thetrouper/sentinel/Sentinel.java +++ b/src/main/java/io/github/thetrouper/sentinel/Sentinel.java @@ -10,8 +10,10 @@ import io.github.thetrouper.sentinel.events.*; import io.github.thetrouper.sentinel.server.functions.AntiSpam; import io.github.thetrouper.sentinel.server.functions.Authenticator; import io.github.thetrouper.sentinel.server.functions.ProfanityFilter; +import io.github.thetrouper.sentinel.server.functions.Telemetry; import io.github.thetrouper.sentinel.server.util.FileUtils; import io.github.thetrouper.sentinel.server.util.Randomizer; +import net.md_5.bungee.api.chat.ClickEvent; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; @@ -32,6 +34,8 @@ public final class Sentinel extends JavaPlugin { public static String prefix = ""; public static String key = ""; public static final Logger log = Bukkit.getLogger(); + public static String identifier = ""; + public static boolean usesDynamicIP; /** * Plugin startup logic @@ -42,6 +46,7 @@ public final class Sentinel extends JavaPlugin { instance = this; Config.loadConfiguration(); String serverID = Authenticator.getServerID(); + identifier = serverID; log.info("\n]====---- Requesting Authentication ----====[ \n- license Key: " + key + " \n- Server ID: " + serverID); String authStatus = "ERROR"; try { @@ -51,54 +56,21 @@ public final class Sentinel extends JavaPlugin { } switch (authStatus) { case "AUTHORIZED" -> { - log.info("]======----- Auth Success! -----======["); - // Init - getConfig().options().copyDefaults(); - saveResource("nbt-config.yml", false); - saveResource("false-positives.yml", false); - saveResource("strict.yml", false); - saveResource("swears.yml", false); - saveDefaultConfig(); - - - // Plugin startup logic - log.info("Sentinel has loaded! (" + getDescription().getVersion() + ")"); - - // Enable Functions - AntiSpam.enableAntiSpam(); - ProfanityFilter.enableAntiSwear(); - - prefix = Config.Plugin.getPrefix(); - - // Commands -> BE SURE TO REGISTER ANY NEW COMMANDS IN PLUGIN.YML (src/main/java/resources/plugin.yml)! - new SentinelCommand().register(); - new MessageCommand().register(); - new ReplyCommand().register(); - new ReopCommand().register(); - new SocialSpyCommand().register(); - - // Events - manager.registerEvents(new CommandEvent(),this); - manager.registerEvents(new CMDBlockExecute(), this); - manager.registerEvents(new CMDBlockPlace(), this); - manager.registerEvents(new CMDBlockUse(), this); - manager.registerEvents(new CMDMinecartPlace(), this); - manager.registerEvents(new CMDMinecartUse(), this); - manager.registerEvents(new NBTEvents(), this); - manager.registerEvents(new ChatEvent(),this); - - // Scheduled timers - Bukkit.getScheduler().runTaskTimer(this, AntiSpam::decayHeat,0, 20); - Bukkit.getScheduler().runTaskTimer(this, ProfanityFilter::decayScore,0,1200); - log.info("\n" + - " ____ __ ___ \n" + - "/\\ _`\\ /\\ \\__ __ /\\_ \\ \n" + - "\\ \\,\\L\\_\\ __ ___\\ \\ ,_\\/\\_\\ ___ __\\//\\ \\ \n" + - " \\/_\\__ \\ /'__`\\/' _ `\\ \\ \\/\\/\\ \\ /' _ `\\ /'__`\\\\ \\ \\ \n" + - " /\\ \\L\\ \\/\\ __//\\ \\/\\ \\ \\ \\_\\ \\ \\/\\ \\/\\ \\/\\ __/ \\_\\ \\_ \n" + - " \\ `\\____\\ \\____\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\_\\ \\____\\/\\____\\\n" + - " \\/_____/\\/____/\\/_/\\/_/\\/__/ \\/_/\\/_/\\/_/\\/____/\\/____/\n" + - " ]====---- Advanced Anti-Grief & Chat Filter ----====["); + startup(); + } + case "MINEHUT" -> { + usesDynamicIP = true; + String minehutStatus = Telemetry.loadTelemetryHook(serverID, key); + switch (minehutStatus) { + case "SUCCESS" -> { + log.info("Dynamic IP auth Success!"); + startup(); + } + case "FAILURE" -> { + log.info("Dynamic IP Failure. Webhook Error possible? Please contact obvwolf to fix this."); + getServer().getPluginManager().disablePlugin(this); + } + } } case "INVALID-ID" -> { log.info("Authentication Failure, You have not whitelisted this server ID yet."); @@ -115,6 +87,53 @@ public final class Sentinel extends JavaPlugin { } } + private void startup() { + log.info("\n]======----- Auth Success! -----======["); + // Init + getConfig().options().copyDefaults(); + saveDefaultConfig(); + + + // Plugin startup logic + log.info("Sentinel has loaded! (" + getDescription().getVersion() + ")"); + + // Enable Functions + AntiSpam.enableAntiSpam(); + ProfanityFilter.enableAntiSwear(); + + prefix = Config.Plugin.getPrefix(); + + // Commands -> BE SURE TO REGISTER ANY NEW COMMANDS IN PLUGIN.YML (src/main/java/resources/plugin.yml)! + new SentinelCommand().register(); + new MessageCommand().register(); + new ReplyCommand().register(); + new ReopCommand().register(); + new SocialSpyCommand().register(); + + // Events + manager.registerEvents(new CommandEvent(),this); + manager.registerEvents(new CMDBlockExecute(), this); + manager.registerEvents(new CMDBlockPlace(), this); + manager.registerEvents(new CMDBlockUse(), this); + manager.registerEvents(new CMDMinecartPlace(), this); + manager.registerEvents(new CMDMinecartUse(), this); + manager.registerEvents(new NBTEvents(), this); + manager.registerEvents(new ChatEvent(),this); + + // Scheduled timers + Bukkit.getScheduler().runTaskTimer(this, AntiSpam::decayHeat,0, 20); + Bukkit.getScheduler().runTaskTimer(this, ProfanityFilter::decayScore,0,1200); + log.info("\n" + + " ____ __ ___ \n" + + "/\\ _`\\ /\\ \\__ __ /\\_ \\ \n" + + "\\ \\,\\L\\_\\ __ ___\\ \\ ,_\\/\\_\\ ___ __\\//\\ \\ \n" + + " \\/_\\__ \\ /'__`\\/' _ `\\ \\ \\/\\/\\ \\ /' _ `\\ /'__`\\\\ \\ \\ \n" + + " /\\ \\L\\ \\/\\ __//\\ \\/\\ \\ \\ \\_\\ \\ \\/\\ \\/\\ \\/\\ __/ \\_\\ \\_ \n" + + " \\ `\\____\\ \\____\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\_\\ \\____\\/\\____\\\n" + + " \\/_____/\\/____/\\/_/\\/_/\\/__/ \\/_/\\/_/\\/_/\\/____/\\/____/\n" + + " ]====---- Advanced Anti-Grief & Chat Filter ----====["); + } + /** * Plugin shutdown logic */ @@ -122,9 +141,7 @@ public final class Sentinel extends JavaPlugin { public void onDisable() { // Plugin shutdown logic log.info("Sentinel has disabled! (" + getDescription().getVersion() + ") Your server is now no longer protected!"); - } - public static File getDF() { - return getInstance().getDataFolder(); + Telemetry.sendShutdownLog(identifier,key); } /** diff --git a/src/main/java/io/github/thetrouper/sentinel/data/Config.java b/src/main/java/io/github/thetrouper/sentinel/data/Config.java index 73e539d..201799c 100644 --- a/src/main/java/io/github/thetrouper/sentinel/data/Config.java +++ b/src/main/java/io/github/thetrouper/sentinel/data/Config.java @@ -4,12 +4,16 @@ package io.github.thetrouper.sentinel.data; +import com.google.common.base.Charsets; import io.github.thetrouper.sentinel.Sentinel; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,12 +22,12 @@ import java.util.Map; * Config loader */ public abstract class Config { - private static final FileConfiguration mainConfig = Sentinel.getInstance().getConfig(); - private static final FileConfiguration nbtConfig = YamlConfiguration.loadConfiguration(new File(Sentinel.getDF() + "nbt-config.yml")); - private static final FileConfiguration falsePositives = YamlConfiguration.loadConfiguration(new File(Sentinel.getDF() + "false-positives.yml")); - private static final FileConfiguration strictWords = YamlConfiguration.loadConfiguration(new File(Sentinel.getDF() + "strict.yml")); - private static final FileConfiguration swearWords = YamlConfiguration.loadConfiguration(new File(Sentinel.getDF() + "swears")); + private static final FileConfiguration mainConfig = Sentinel.getInstance().getConfig(); + private static final FileConfiguration nbtConfig = getConfig("nbt-config.yml"); + private static final FileConfiguration falsePositives = getConfig("false-positives.yml"); + private static final FileConfiguration strictWords = getConfig("strict.yml"); + private static final FileConfiguration swearWords = getConfig("swears.yml"); public static List getPunishCommands() { return punishCommands; @@ -137,7 +141,15 @@ public abstract class Config { public static List swearBlacklist; public static List slurs; public static Map leetPatterns; + public static FileConfiguration getConfig(String fileName) { + File configFile = new File(Sentinel.getInstance().getDataFolder(), fileName); + if (!configFile.exists()) { + Sentinel.getInstance().saveResource(fileName, false); + } + + return YamlConfiguration.loadConfiguration(configFile); + } public static void loadConfiguration() { Sentinel.prefix = mainConfig.getString("config.plugin.prefix"); diff --git a/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java b/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java index 0746a2f..8d2bb45 100644 --- a/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java +++ b/src/main/java/io/github/thetrouper/sentinel/events/ChatEvent.java @@ -14,8 +14,23 @@ public class ChatEvent implements Listener { @EventHandler public static void onChat(AsyncPlayerChatEvent e) { if (e.isCancelled()) return; - if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antiswear.bypass")) if (Config.antiSwearEnabled) ProfanityFilter.handleProfanityFilter(e); - if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antispam.bypass")) if (Config.antiSpamEnabled) AntiSpam.handleAntiSpam(e); - if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antiunicode.bypass")) if (Config.antiUnicode) AntiUnicode.handleAntiUnicode(e); + if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antiunicode.bypass")) { + if (Config.antiUnicode) { + AntiUnicode.handleAntiUnicode(e); + return; + } + } + if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antiswear.bypass")) { + if (Config.antiSwearEnabled) { + ProfanityFilter.handleProfanityFilter(e); + return; + } + } + if (!Sentinel.isTrusted(e.getPlayer()) || !e.getPlayer().hasPermission("sentinel.chat.antispam.bypass")) { + if (Config.antiSpamEnabled) { + AntiSpam.handleAntiSpam(e); + return; + } + } } } diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/Authenticator.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/Authenticator.java index b914dc6..d913f84 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/Authenticator.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/Authenticator.java @@ -58,16 +58,22 @@ public class Authenticator { if (key.equals(licenseKey)) { if (Arrays.asList(allowedArr).contains(serverID)) { authStatus = "AUTHORIZED"; + return authStatus; } else { + if (Arrays.asList(allowedArr).contains("minehut")) { + authStatus = "MINEHUT"; + return authStatus; + } authStatus = "INVALID-ID"; + return authStatus; } - break; } } } if (authStatus.isEmpty()) { authStatus = "UNREGISTERED"; + return authStatus; } } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/Message.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/Message.java index 821d104..98f9a53 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/Message.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/Message.java @@ -1,19 +1,33 @@ package io.github.thetrouper.sentinel.server.functions; +import io.github.thetrouper.sentinel.Sentinel; import io.github.thetrouper.sentinel.commands.MessageCommand; import io.github.thetrouper.sentinel.commands.SocialSpyCommand; +import io.github.thetrouper.sentinel.data.Config; import io.github.thetrouper.sentinel.server.util.ServerUtils; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.hover.content.Text; import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import java.util.HashSet; import java.util.Map; import java.util.UUID; public class Message { private static Map replyMap = MessageCommand.replyMap; public static void messagePlayer(Player sender, Player receiver, String message) { + HashSet receivers = new HashSet<>(); + receivers.add(receiver); + AsyncPlayerChatEvent checkEvent = new AsyncPlayerChatEvent(true,sender,message,receivers); + if (!Sentinel.isTrusted(sender) || !sender.hasPermission("sentinel.chat.antiswear.bypass")) if (Config.antiSwearEnabled) ProfanityFilter.handleProfanityFilter(checkEvent); + if (!Sentinel.isTrusted(sender) || !sender.hasPermission("sentinel.chat.antispam.bypass")) if (Config.antiSpamEnabled) AntiSpam.handleAntiSpam(checkEvent); + if (!Sentinel.isTrusted(sender) || !sender.hasPermission("sentinel.chat.antiunicode.bypass")) if (Config.antiUnicode) AntiUnicode.handleAntiUnicode(checkEvent); + if (checkEvent.isCancelled()) { + return; + } + sender.sendMessage("§d§lMessage §8» §b[§fYou §e>§f " + receiver.getName() + "§b] §7" + message); receiver.sendMessage("§d§lMessage §8» §b[§f" + sender.getName() + " §e>§f You§b] §7" + message); replyMap.put(receiver.getUniqueId(),sender.getUniqueId()); diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java index 6f8d143..3b090f7 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/ProfanityFilter.java @@ -41,7 +41,6 @@ public class ProfanityFilter { ServerUtils.sendDebugMessage("AntiSwear Flag, Message: " + message + " Concentrated: " + fullSimplify(message) + " Severity: " + severity + " Previous Score: " + scoreMap.get(p) +" Adding Score: " + Config.lowScore); scoreMap.put(p, scoreMap.get(p) + Config.lowScore); e.setCancelled(true); - p.sendMessage(TextUtils.prefix("§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute!")); blockSwear(p,highlightProfanity(message),message,severity); } case "medium-low" -> { @@ -230,12 +229,14 @@ public class ProfanityFilter { } private static boolean containsSwears(String text) { + ServerUtils.sendDebugMessage("Debug: [AntiSwear] Checking for swears: " + swearBlacklist.toString()); for (String swear : swearBlacklist) { if (text.contains(swear)) return true; } return false; } private static boolean containsSlurs(String text) { + ServerUtils.sendDebugMessage("Debug: [AntiSwear] Checking for slurs: " + slurs.toString()); for (String slur : slurs) { if (text.contains(slur)) return true; } diff --git a/src/main/java/io/github/thetrouper/sentinel/server/functions/Telemetry.java b/src/main/java/io/github/thetrouper/sentinel/server/functions/Telemetry.java new file mode 100644 index 0000000..86034df --- /dev/null +++ b/src/main/java/io/github/thetrouper/sentinel/server/functions/Telemetry.java @@ -0,0 +1,156 @@ +package io.github.thetrouper.sentinel.server.functions; + +import io.github.thetrouper.sentinel.Sentinel; +import io.github.thetrouper.sentinel.data.Config; +import io.github.thetrouper.sentinel.discord.DiscordWebhook; +import io.github.thetrouper.sentinel.server.util.ArrayUtils; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.awt.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.*; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.List; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +public class Telemetry { + + public Telemetry() throws UnknownHostException { + } + static InetAddress IP; + + static { + try { + IP = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + + public static String telemetryHook; + + public static String loadTelemetryHook(String serverID, String licenseKey) { + String hook = ""; + try { + URL url = new URL("https://sentinelauth.000webhostapp.com/telemetrykey.html"); + BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); + List lines = readLines(reader); + + for (String line : lines) { + if (line.contains("data-id")) { + hook = extractValue(line, "data-hook"); + telemetryHook = hook; + Map response = sendStartupLog(serverID,licenseKey); + + if (response.containsKey("SUCCESS")) { + Sentinel.log.info("Successfully grabbed telemetry hook"); + return "SUCCESS"; + } else { + Sentinel.log.info("An Error occurred while attempting to connect to the telemetry hook: " + response.get("ERROR")); + return "FAIL"; + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return "ERROR"; + } + + public static Map testWebhook(String hook) { + Map response = new HashMap<>(); + response.put("SUCCESS", "NULL"); + DiscordWebhook webhook = new DiscordWebhook(hook); + DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() + .setAuthor("Test Success!", "", "") + .setDescription("Connected to webhook") + .setColor(Color.GREEN); + webhook.addEmbed(embed); + try { + webhook.execute(); + } catch (IOException e) { + response.clear(); + response.put("ERROR", e.toString()); + return response; + } + return response; + } + + public static List readLines(BufferedReader reader) { + try { + List lines = new ArrayList<>(); + String line = reader.readLine(); + while (line != null) { + lines.add(line); + line = reader.readLine(); + } + reader.close(); + return lines; + } catch (Exception ex) { + ex.printStackTrace(); + } + return new ArrayList<>(); + } + + public static String extractValue(String line, String attribute) { + int start = line.indexOf(attribute + "=\"") + attribute.length() + 2; + int end = line.indexOf("\"", start); + return line.substring(start, end); + } + + public static Map sendStartupLog(String serverID, String licenseKey) { + Map response = new HashMap<>(); + response.put("SUCCESS", "NULL"); + DiscordWebhook webhook = new DiscordWebhook(telemetryHook); + DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() + .setAuthor("Server Startup Log", "", "") + .setTitle("Dynamic IP server connected") + .setDescription("License key: `"+ licenseKey + "`\\n" + + "Server ID: `" + serverID + "`") + .setColor(Color.GREEN); + webhook.addEmbed(embed); + try { + webhook.execute(); + } catch (IOException e) { + response.clear(); + response.put("ERROR", e.toString()); + return response; + } + return response; + + } + public static Map sendShutdownLog(String serverID, String licenseKey) { + Map response = new HashMap<>(); + response.put("SUCCESS", "NULL"); + DiscordWebhook webhook = new DiscordWebhook(telemetryHook); + DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject() + .setAuthor("Server Shutdown Log", "", "") + .setTitle("Dynamic IP server disconnected") + .setDescription("License key: `"+ licenseKey + "`\\n" + + "Server ID: `" + serverID + "`") + .setColor(Color.RED); + webhook.addEmbed(embed); + try { + webhook.execute(); + } catch (IOException e) { + response.clear(); + response.put("ERROR", e.toString()); + return response; + } + return response; + + } +} diff --git a/src/main/java/io/github/thetrouper/sentinel/server/util/FileUtils.java b/src/main/java/io/github/thetrouper/sentinel/server/util/FileUtils.java index f030a1e..cafafd7 100644 --- a/src/main/java/io/github/thetrouper/sentinel/server/util/FileUtils.java +++ b/src/main/java/io/github/thetrouper/sentinel/server/util/FileUtils.java @@ -30,18 +30,18 @@ import java.util.UUID; import org.bukkit.Location; public class FileUtils { public static boolean folderExists(String folderName) { - File folder = new File(Sentinel.getDF(), folderName); + File folder = new File(Sentinel.getInstance().getDataFolder(), folderName); return folder.exists() && folder.isDirectory(); } public static void createFolder(String folderName) { - File folder = new File(Sentinel.getDF(), folderName); + File folder = new File(Sentinel.getInstance().getDataFolder(), folderName); if (!folder.exists()) { folder.mkdirs(); } } public static String createNBTLog(String contents) { String fileName = "nbt_log-" + Randomizer.generateID(); - File file = new File(Sentinel.getDF() + "/LoggedNBT/" + fileName + ".txt"); + File file = new File(Sentinel.getInstance().getDataFolder() + "/LoggedNBT/" + fileName + ".txt"); try { if (!file.exists()) { file.createNewFile(); @@ -59,7 +59,7 @@ public class FileUtils { public static String createCommandLog(String command) { String fileName = "command_log-" + Randomizer.generateID(); - File file = new File(Sentinel.getDF() + "/LoggedCommands/" + fileName + ".txt"); + File file = new File(Sentinel.getInstance().getDataFolder() + "/LoggedCommands/" + fileName + ".txt"); try { if (!file.exists()) { file.createNewFile();