Anti-Spam roots have been created!

This commit is contained in:
TheTrouper
2023-07-03 09:13:57 -05:00
parent 9257f3d5d2
commit 142641d647
16 changed files with 815 additions and 58 deletions

View File

@@ -3,7 +3,7 @@ plugins {
}
group = 'io.github.thetrouper'
version = '0.0.1'
version = '0.0.2'
repositories {
mavenCentral()

View File

@@ -7,9 +7,11 @@ package io.github.thetrouper.sentinel;
import io.github.thetrouper.sentinel.commands.InfoCommand;
import io.github.thetrouper.sentinel.commands.ReopCommand;
import io.github.thetrouper.sentinel.data.Config;
import io.github.thetrouper.sentinel.events.ChatEvent;
import io.github.thetrouper.sentinel.events.CmdBlockEvents;
import io.github.thetrouper.sentinel.events.CommandEvent;
import io.github.thetrouper.sentinel.events.NBTEvents;
import io.github.thetrouper.sentinel.server.functions.AntiSpam;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
@@ -56,6 +58,24 @@ public final class Sentinel extends JavaPlugin {
// Plugin startup logic
loadConfiguration();
log.info("Sentinel has loaded! (" + getDescription().getVersion() + ")");
// Enable Functions
AntiSpam.enableAntiSpam();
prefix = Config.Plugin.getPrefix();
// Commands -> BE SURE TO REGISTER ANY NEW COMMANDS IN PLUGIN.YML (src/main/java/resources/plugin.yml)!
getCommand("sentinel").setExecutor(new InfoCommand());
getCommand("sentinel").setTabCompleter(new InfoCommand());
getCommand("reop").setExecutor(new ReopCommand());
// Events
manager.registerEvents(new CommandEvent(),this);
manager.registerEvents(new CmdBlockEvents(), this);
manager.registerEvents(new NBTEvents(), this);
manager.registerEvents(new ChatEvent(),this);
// Scheduled timers
Bukkit.getScheduler().runTaskTimer(this, AntiSpam::decayHeat,0, 20);
log.info("\n" +
" ____ __ ___ \n" +
"/\\ _`\\ /\\ \\__ __ /\\_ \\ \n" +
@@ -64,18 +84,7 @@ public final class Sentinel extends JavaPlugin {
" /\\ \\L\\ \\/\\ __//\\ \\/\\ \\ \\ \\_\\ \\ \\/\\ \\/\\ \\/\\ __/ \\_\\ \\_ \n" +
" \\ `\\____\\ \\____\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\_\\ \\____\\/\\____\\\n" +
" \\/_____/\\/____/\\/_/\\/_/\\/__/ \\/_/\\/_/\\/_/\\/____/\\/____/\n" +
" ]======------ Advanced Anti-Grief ------======[");
prefix = Config.Plugin.getPrefix();
// Commands -> BE SURE TO REGISTER ANY NEW COMMANDS IN PLUGIN.YML (src/main/java/resources/plugin.yml)!
getCommand("sentinel").setExecutor(new InfoCommand());
getCommand("sentinel").setTabCompleter(new InfoCommand.Tabs());
getCommand("reop").setExecutor(new ReopCommand());
// Events
manager.registerEvents(new CommandEvent(),this);
manager.registerEvents(new CmdBlockEvents(), this);
manager.registerEvents(new NBTEvents(), this);
" ]====---- Advanced Anti-Grief & Chat Filter ----====[");
}
/**
@@ -118,7 +127,7 @@ public final class Sentinel extends JavaPlugin {
dangerousCommands = config.getStringList("config.plugin.dangerous");
// Load log protected commands
logDangerousCommands = config.getBoolean("config.plugin.log-protected");
logDangerousCommands = config.getBoolean("config.plugin.log-dangerous");
// Load logged commands
loggedCommands = config.getStringList("config.plugin.logged");

View File

@@ -4,12 +4,11 @@
package io.github.thetrouper.sentinel.commands;
import io.github.thetrouper.sentinel.discord.WebhookSender;
import io.github.thetrouper.sentinel.exceptions.CmdExHandler;
import io.github.thetrouper.sentinel.server.functions.AntiSpam;
import io.github.thetrouper.sentinel.server.util.TextUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.*;
import java.util.ArrayList;
import java.util.List;
@@ -17,33 +16,45 @@ import java.util.List;
/**
* Example command
*/
public class InfoCommand implements CommandExecutor {
public class InfoCommand implements TabExecutor {
public static boolean debugmode;
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
try {
sender.sendMessage(TextUtils.prefix("This server is running Sentinel by TheTrouper. Hot reloading is not advised."));
if (args.length == 0) {
sender.sendMessage(TextUtils.prefix("&cYou must specify an item to give."));
return true;
}
switch (args[0]) {
case "debugmode" -> {
debugmode = !debugmode;
sender.sendMessage(TextUtils.prefix("Debug mode set to §b" + debugmode));
}
case "webhooktest" -> {
sender.sendMessage(TextUtils.prefix("Testing the webhook..."));
WebhookSender.sendEmbedWarning(sender.getName(), "/sentinel webhooktest",true,true,false);
WebhookSender.sendHelloWorldEmbed();
}
case "checkheat" -> {
sender.sendMessage(TextUtils.prefix("Your heat is §e" + AntiSpam.heatMap.get(sender).toString()));
}
}
return true;
} catch (Exception ex) {
CmdExHandler handler = new CmdExHandler(ex,command);
sender.sendMessage(handler.getErrorMessage());
ex.printStackTrace();
return true;
}
}
/**
* Example command's tab completer
*/
public static class Tabs implements TabCompleter {
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> list = new ArrayList<>();
switch (args.length) {
case 1 -> list.add("reload");
}
list.removeIf(s -> !s.toLowerCase().contains(args[args.length - 1].toLowerCase()));
return list;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return new TabComplBuilder(sender,command,alias,args)
.add(1,new String[]{
"debugmode",
"webhooktest",
"checkheat"
}).build();
}
}

View File

@@ -0,0 +1,60 @@
package io.github.thetrouper.sentinel.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.*;
public class TabComplBuilder {
private Map<Integer,List<String>> entries = new HashMap<>();
private final CommandSender sender;
private final Command command;
private final String alias;
private final String[] args;
public TabComplBuilder(CommandSender sender, Command command, String alias, String[] args) {
this.sender = sender;
this.command = command;
this.alias = alias;
this.args = args;
}
/**
* Adds to the tab completion
* @param atIndex should be a number above or equal to 1
* @param entry string array
* @param condition condition
*/
public TabComplBuilder add(int atIndex, String[] entry, boolean condition) {
if (condition) add(atIndex,entry);
return this;
}
/**
* Adds to the tab completion
* @param atIndex should be a number above or equal to 1
* @param entry string array
*/
public TabComplBuilder add(int atIndex, String[] entry) {
atIndex = Math.max(1,atIndex);
entries.put(atIndex,Arrays.stream(entry).toList());
return this;
}
public TabComplBuilder add(int atIndex, List<String> entry, boolean condition) {
if (condition) add(atIndex,entry);
return this;
}
public TabComplBuilder add(int atIndex, List<String> entry) {
entries.put(atIndex,entry);
return this;
}
public List<String> build() {
List<String> list = new ArrayList<>(entries.get(args.length) != null ? entries.get(args.length) : new ArrayList<>());
list.removeIf(s -> !s.toLowerCase().contains(args[args.length - 1].toLowerCase()));
return list;
}
}

View File

@@ -0,0 +1,9 @@
package io.github.thetrouper.sentinel.data;
public class Emojis {
public static String success = "<:success:1125240412081238066>";
public static String failure = "<:failure:1125241087909429369>";
public static String rightArrow = "<:rightArrow:1125241843597189160>";
public static String nuke = "<:nuke:1125244368807280702>";
public static String member = "<:member:1125244044407218176>";
}

View File

@@ -0,0 +1,390 @@
package io.github.thetrouper.sentinel.discord;
import javax.net.ssl.HttpsURLConnection;
import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Class used to execute Discord Webhooks with low effort
*/
public class DiscordWebhook {
private final String url;
private String content;
private String username;
private String avatarUrl;
private boolean tts;
private List<EmbedObject> embeds = new ArrayList<>();
/**
* Constructs a new DiscordWebhook instance
*
* @param url The webhook URL obtained in Discord
*/
public DiscordWebhook(String url) {
this.url = url;
}
public void setContent(String content) {
this.content = content;
}
public void setUsername(String username) {
this.username = username;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public void setTts(boolean tts) {
this.tts = tts;
}
public void addEmbed(EmbedObject embed) {
this.embeds.add(embed);
}
public void execute() throws IOException {
if (this.content == null && this.embeds.isEmpty()) {
throw new IllegalArgumentException("Set content or add at least one EmbedObject");
}
JSONObject json = new JSONObject();
json.put("content", this.content);
json.put("username", this.username);
json.put("avatar_url", this.avatarUrl);
json.put("tts", this.tts);
if (!this.embeds.isEmpty()) {
List<JSONObject> embedObjects = new ArrayList<>();
for (EmbedObject embed : this.embeds) {
JSONObject jsonEmbed = new JSONObject();
jsonEmbed.put("title", embed.getTitle());
jsonEmbed.put("description", embed.getDescription());
jsonEmbed.put("url", embed.getUrl());
if (embed.getColor() != null) {
Color color = embed.getColor();
int rgb = color.getRed();
rgb = (rgb << 8) + color.getGreen();
rgb = (rgb << 8) + color.getBlue();
jsonEmbed.put("color", rgb);
}
EmbedObject.Footer footer = embed.getFooter();
EmbedObject.Image image = embed.getImage();
EmbedObject.Thumbnail thumbnail = embed.getThumbnail();
EmbedObject.Author author = embed.getAuthor();
List<EmbedObject.Field> fields = embed.getFields();
if (footer != null) {
JSONObject jsonFooter = new JSONObject();
jsonFooter.put("text", footer.getText());
jsonFooter.put("icon_url", footer.getIconUrl());
jsonEmbed.put("footer", jsonFooter);
}
if (image != null) {
JSONObject jsonImage = new JSONObject();
jsonImage.put("url", image.getUrl());
jsonEmbed.put("image", jsonImage);
}
if (thumbnail != null) {
JSONObject jsonThumbnail = new JSONObject();
jsonThumbnail.put("url", thumbnail.getUrl());
jsonEmbed.put("thumbnail", jsonThumbnail);
}
if (author != null) {
JSONObject jsonAuthor = new JSONObject();
jsonAuthor.put("name", author.getName());
jsonAuthor.put("url", author.getUrl());
jsonAuthor.put("icon_url", author.getIconUrl());
jsonEmbed.put("author", jsonAuthor);
}
List<JSONObject> jsonFields = new ArrayList<>();
for (EmbedObject.Field field : fields) {
JSONObject jsonField = new JSONObject();
jsonField.put("name", field.getName());
jsonField.put("value", field.getValue());
jsonField.put("inline", field.isInline());
jsonFields.add(jsonField);
}
jsonEmbed.put("fields", jsonFields.toArray());
embedObjects.add(jsonEmbed);
}
json.put("embeds", embedObjects.toArray());
}
URL url = new URL(this.url);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("User-Agent", "Java-DiscordWebhook-BY-Gelox_");
connection.setDoOutput(true);
connection.setRequestMethod("POST");
OutputStream stream = connection.getOutputStream();
stream.write(json.toString().getBytes());
stream.flush();
stream.close();
connection.getInputStream().close(); //I'm not sure why but it doesn't work without getting the InputStream
connection.disconnect();
}
public static class EmbedObject {
private String title;
private String description;
private String url;
private Color color;
private Footer footer;
private Thumbnail thumbnail;
private Image image;
private Author author;
private List<Field> fields = new ArrayList<>();
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getUrl() {
return url;
}
public Color getColor() {
return color;
}
public Footer getFooter() {
return footer;
}
public Thumbnail getThumbnail() {
return thumbnail;
}
public Image getImage() {
return image;
}
public Author getAuthor() {
return author;
}
public List<Field> getFields() {
return fields;
}
public EmbedObject setTitle(String title) {
this.title = title;
return this;
}
public EmbedObject setDescription(String description) {
this.description = description;
return this;
}
public EmbedObject setUrl(String url) {
this.url = url;
return this;
}
public EmbedObject setColor(Color color) {
this.color = color;
return this;
}
public EmbedObject setFooter(String text, String icon) {
this.footer = new Footer(text, icon);
return this;
}
public EmbedObject setThumbnail(String url) {
this.thumbnail = new Thumbnail(url);
return this;
}
public EmbedObject setImage(String url) {
this.image = new Image(url);
return this;
}
public EmbedObject setAuthor(String name, String url, String icon) {
this.author = new Author(name, url, icon);
return this;
}
public EmbedObject addField(String name, String value, boolean inline) {
this.fields.add(new Field(name, value, inline));
return this;
}
private class Footer {
private String text;
private String iconUrl;
private Footer(String text, String iconUrl) {
this.text = text;
this.iconUrl = iconUrl;
}
private String getText() {
return text;
}
private String getIconUrl() {
return iconUrl;
}
}
private class Thumbnail {
private String url;
private Thumbnail(String url) {
this.url = url;
}
private String getUrl() {
return url;
}
}
private class Image {
private String url;
private Image(String url) {
this.url = url;
}
private String getUrl() {
return url;
}
}
private class Author {
private String name;
private String url;
private String iconUrl;
private Author(String name, String url, String iconUrl) {
this.name = name;
this.url = url;
this.iconUrl = iconUrl;
}
private String getName() {
return name;
}
private String getUrl() {
return url;
}
private String getIconUrl() {
return iconUrl;
}
}
private class Field {
private String name;
private String value;
private boolean inline;
private Field(String name, String value, boolean inline) {
this.name = name;
this.value = value;
this.inline = inline;
}
private String getName() {
return name;
}
private String getValue() {
return value;
}
private boolean isInline() {
return inline;
}
}
}
private class JSONObject {
private final HashMap<String, Object> map = new HashMap<>();
void put(String key, Object value) {
if (value != null) {
map.put(key, value);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
Set<Map.Entry<String, Object>> entrySet = map.entrySet();
builder.append("{");
int i = 0;
for (Map.Entry<String, Object> entry : entrySet) {
Object val = entry.getValue();
builder.append(quote(entry.getKey())).append(":");
if (val instanceof String) {
builder.append(quote(String.valueOf(val)));
} else if (val instanceof Integer) {
builder.append(Integer.valueOf(String.valueOf(val)));
} else if (val instanceof Boolean) {
builder.append(val);
} else if (val instanceof JSONObject) {
builder.append(val.toString());
} else if (val.getClass().isArray()) {
builder.append("[");
int len = Array.getLength(val);
for (int j = 0; j < len; j++) {
builder.append(Array.get(val, j).toString()).append(j != len - 1 ? "," : "");
}
builder.append("]");
}
builder.append(++i == entrySet.size() ? "}" : ",");
}
return builder.toString();
}
private String quote(String string) {
return "\"" + string + "\"";
}
}
}

View File

@@ -1,5 +0,0 @@
package io.github.thetrouper.sentinel.discord;
public class WebHook {
// To be implemented once I have learned how to use JDA
}

View File

@@ -0,0 +1,125 @@
package io.github.thetrouper.sentinel.discord;
import io.github.thetrouper.sentinel.Sentinel;
import io.github.thetrouper.sentinel.commands.InfoCommand;
import io.github.thetrouper.sentinel.data.Emojis;
import io.github.thetrouper.sentinel.discord.DiscordWebhook;
import io.github.thetrouper.sentinel.server.util.ServerUtils;
import io.github.thetrouper.sentinel.server.util.TextUtils;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.io.IOException;
public class WebhookSender {
public static void sendHelloWorldEmbed() {
String webhookUrl = Sentinel.webhook;
// Create a new DiscordWebhook instance
DiscordWebhook webhook = new DiscordWebhook(webhookUrl);
// Create an EmbedObject and set its properties
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject()
.setDescription("Hello, World!")
.setColor(Color.GREEN);
// Add the EmbedObject to the webhook
webhook.addEmbed(embed);
try {
// Execute the webhook
webhook.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String successOrFail(boolean bool) {
if (bool) {
return Emojis.success;
} else {
return Emojis.failure;
}
}
public static void sendEmbedWarning(String player, String command, boolean denied, boolean removedOp, boolean banned) {
ServerUtils.sendDebugMessage("Creating Command Webhook...");
final String description =
Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" +
Emojis.rightArrow + " **Command:** " + command + " " + Emojis.nuke + "\\n" +
Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" +
Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" +
Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n";
DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook);
webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png");
webhook.setUsername("Sentinel Anti-Nuke | Logs");
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject()
.setAuthor("Anti-Nuke has been triggered","","")
.setTitle("The use of a dangerous command has been detected!")
.setDescription(description)
.setColor(Color.RED);
webhook.addEmbed(embed);
try {
ServerUtils.sendDebugMessage("Executing webhook...");
webhook.execute();
} catch (IOException e) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Epic webhook failure!!!"));
Sentinel.log.info(e.toString());
}
}
public static void sendEmbedWarning(String player, Block b, boolean denied, boolean removedOp, boolean banned) {
ServerUtils.sendDebugMessage("Creating Block Webhook...");
final String description =
Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" +
Emojis.rightArrow + " **Block:** " + b.getType() + " " + Emojis.nuke + "\\n" +
Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" +
Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" +
Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n";
DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook);
webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png");
webhook.setUsername("Sentinel Anti-Nuke | Logs");
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject()
.setAuthor("Anti-Nuke has been triggered","","")
.setTitle("The use of a dangerous block has been detected!")
.setDescription(description)
.setColor(Color.RED);
webhook.addEmbed(embed);
try {
ServerUtils.sendDebugMessage("Executing webhook...");
webhook.execute();
} catch (IOException e) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Epic webhook failure!!!"));
Sentinel.log.info(e.toString());
}
}
public static void sendEmbedWarning(String player, ItemStack item, boolean denied, boolean removedOp, boolean banned) {
ServerUtils.sendDebugMessage("Creating Webhook...");
final String description =
Emojis.rightArrow + " **Player:** " + player + " " + Emojis.member + "\\n" +
Emojis.rightArrow + " **Item:** " + item.getType() + " " + Emojis.nuke + "\\n" +
Emojis.rightArrow + " **Denied:** " + successOrFail(denied) + "\\n" +
Emojis.rightArrow + " **Removed OP:** " + successOrFail(removedOp) + "\\n" +
Emojis.rightArrow + " **Banned:** " + successOrFail(banned) + "\\n";
DiscordWebhook webhook = new DiscordWebhook(Sentinel.webhook);
webhook.setAvatarUrl("https://r2.e-z.host/d440b58a-ba90-4839-8df6-8bba298cf817/3lwit5nt.png");
webhook.setUsername("Sentinel Anti-Nuke | Logs");
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject()
.setAuthor("Anti-Nuke has been triggered","","")
.setTitle("The use of a dangerous item has been detected!")
.setDescription(description)
.setColor(Color.BLUE);
webhook.addEmbed(embed);
try {
ServerUtils.sendDebugMessage("Executing webhook...");
webhook.execute();
} catch (IOException e) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Epic webhook failure!!!"));
Sentinel.log.info(e.toString());
}
}
}

View File

@@ -0,0 +1,13 @@
package io.github.thetrouper.sentinel.events;
import io.github.thetrouper.sentinel.server.functions.AntiSpam;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
public class ChatEvent implements Listener {
@EventHandler
public static void onChat(AsyncPlayerChatEvent e) {
AntiSpam.handleAntiSpam(e);
}
}

View File

@@ -22,7 +22,7 @@ public class CmdBlockEvents implements Listener {
Player p = e.getPlayer();
if (!Sentinel.isTrusted(p)) {
e.setCancelled(true);
Sentinel.log.info("Use of a command block has been denied for " + p.getName() + " at X:" + b.getX() + " Y:" + b.getY() + " Z:" + b.getZ());
DeniedActions.handleDeniedAction(p,b);
}
}
}
@@ -30,16 +30,16 @@ public class CmdBlockEvents implements Listener {
private void onCMDBlockPlace(BlockPlaceEvent e) {
if (!Sentinel.preventCmdBlocks) return;
Block b = e.getBlockPlaced();
if (b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK) {
if (b.getType() == Material.COMMAND_BLOCK || b.getType() == Material.CHAIN_COMMAND_BLOCK || b.getType() == Material.REPEATING_COMMAND_BLOCK ) {
Player p = e.getPlayer();
if (!Sentinel.isTrusted(p)) {
e.setCancelled(true);
Sentinel.log.info("Placing a command block has been denied for " + p.getName() + " at X:" + b.getX() + " Y:" + b.getY() + " Z:" + b.getZ());
DeniedActions.handleDeniedAction(p,b);
}
}
}
@EventHandler
private void onCMDBlockMinecartPlace(PlayerInteractEntityEvent e) {
private void onCMDBlockMinecartUse(PlayerInteractEntityEvent e) {
if (!Sentinel.preventCmdBlocks) return;
if (e.getRightClicked().getType() == EntityType.MINECART_COMMAND) {
Player p = e.getPlayer();

View File

@@ -2,6 +2,8 @@ package io.github.thetrouper.sentinel.events;
import io.github.thetrouper.sentinel.Sentinel;
import io.github.thetrouper.sentinel.server.util.DeniedActions;
import io.github.thetrouper.sentinel.server.util.ServerUtils;
import io.github.thetrouper.sentinel.server.util.TextUtils;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -13,16 +15,22 @@ public class CommandEvent implements Listener {
private void onCommand(PlayerCommandPreprocessEvent e) {
Player p = e.getPlayer();
String command = e.getMessage().substring(1).split(" ")[0];
ServerUtils.sendDebugMessage(TextUtils.prefix("Checking command"));
if (Sentinel.isDangerousCommand(command)) {
ServerUtils.sendDebugMessage(TextUtils.prefix( "Command is dangerous"));
if (!Sentinel.isTrusted(p)) {
e.setCancelled(true);
DeniedActions.handleDeniedAction(p,command);
ServerUtils.sendDebugMessage(TextUtils.prefix("Command is canceled"));
DeniedActions.handleDeniedAction(p,e.getMessage());
}
}
if (Sentinel.blockSpecificCommands) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Checking command for specific"));
if (command.contains(":")) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Checking is specific"));
if (!Sentinel.isTrusted(p)) {
e.setCancelled(true);
ServerUtils.sendDebugMessage(TextUtils.prefix("Command is canceled"));
DeniedActions.handleDeniedAction(p,command);
}
}

View File

@@ -0,0 +1,47 @@
package io.github.thetrouper.sentinel.server.functions;
import io.github.thetrouper.sentinel.server.util.GPTUtils;
import io.github.thetrouper.sentinel.server.util.TextUtils;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import java.util.HashMap;
import java.util.Map;
import static io.github.thetrouper.sentinel.server.util.GPTUtils.calculateSimilarity;
public class AntiSpam {
public static Map<Player, Integer> heatMap;
public static Map<Player, String> lastMessageMap;
public static void enableAntiSpam() {
heatMap = new HashMap<>();
lastMessageMap = new HashMap<>();
}
public static void handleAntiSpam(AsyncPlayerChatEvent event) {
Player player = event.getPlayer();
String message = event.getMessage();
if (!heatMap.containsKey(player)) heatMap.put(player, 0);
if (heatMap.get(player) > 10) {
event.setCancelled(true);
player.sendMessage(TextUtils.prefix("Rate limit exceeded! Please wait before sending another message."));
return;
}
if (lastMessageMap.containsKey(player)) {
String lastMessage = lastMessageMap.get(player);
double similarity = calculateSimilarity(message, lastMessage);
if (similarity > 0.5) heatMap.put(player, heatMap.get(player) + 3);
if (similarity > 0.9) heatMap.put(player, heatMap.get(player) + 6);
}
lastMessageMap.put(player, message);
}
public static void decayHeat() {
for (Player player : heatMap.keySet()) {
int heat = heatMap.get(player);
if (heat > 0) {
heatMap.put(player, heat - 1);
}
}
}
}

View File

@@ -1,6 +1,7 @@
package io.github.thetrouper.sentinel.server.util;
import io.github.thetrouper.sentinel.Sentinel;
import io.github.thetrouper.sentinel.discord.WebhookSender;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
@@ -13,27 +14,42 @@ import org.bukkit.inventory.ItemStack;
public class DeniedActions {
private static String logMessage;
private static boolean banned;
private static boolean opRemoved;
private static boolean denied;
public static void handleDeniedAction(Player p, String command) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Handling denied command..."));
if (!Sentinel.logDangerousCommands) return;
ServerUtils.sendDebugMessage(TextUtils.prefix("LDC is enabled"));
logMessage = "]==-- Sentinel --==[\n" +
"A Dangerous command has been attempted!\n" +
"Player: " + p.getName() + "\n" +
"Command: " + command + "\n";
if (Sentinel.deop) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Deoping player"));
p.setOp(false);
logMessage = logMessage + "Operator Removed: ✔\n";
opRemoved = true;
} else {
logMessage = logMessage + "Operator Removed: ✘\n";
opRemoved = false;
}
if (Sentinel.ban) {
ServerUtils.sendDebugMessage(TextUtils.prefix("Banning player"));
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting a dangerous command. Contact an administrator if you believe this to be a mistake.");
logMessage = logMessage + "Banned: ✔\n";
banned = true;
} else {
logMessage = logMessage + "Banned: ✘\n";
banned = false;
}
ServerUtils.sendDebugMessage(TextUtils.prefix("Sending log"));
logMessage = logMessage + "Denied: ✔";
denied = true;
Sentinel.log.info(logMessage);
notifyTrusted(p, command);
WebhookSender.sendEmbedWarning(p.getName(),command,denied,opRemoved,banned);
}
public static void handleDeniedAction(Player p, Block block) {
if (!Sentinel.logCmdBlocks) return;
@@ -44,18 +60,24 @@ public class DeniedActions {
if (Sentinel.deop) {
p.setOp(false);
logMessage = logMessage + "Operator Removed: ✔\n";
opRemoved = true;
} else {
logMessage = logMessage + "Operator Removed: ✘\n";
opRemoved = false;
}
if (Sentinel.ban) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting to use dangerous blocks. Contact an administrator if you believe this to be a mistake.");
logMessage = logMessage + "Banned: ✔\n";
banned = true;
} else {
logMessage = logMessage + "Banned: ✘\n";
banned = false;
}
logMessage = logMessage + "Denied: ✔";
denied = true;
Sentinel.log.info(logMessage);
notifyTrusted(p, block);
WebhookSender.sendEmbedWarning(p.getName(),block,denied,opRemoved,banned);
}
public static void handleDeniedAction(Player p, ItemStack i) {
if (!Sentinel.logNBT) return;
@@ -66,18 +88,24 @@ public class DeniedActions {
if (Sentinel.deop) {
p.setOp(false);
logMessage = logMessage + "Operator Removed: ✔\n";
opRemoved = true;
} else {
logMessage = logMessage + "Operator Removed: ✘\n";
opRemoved = false;
}
if (Sentinel.ban) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting a dangerous command. Contact an administrator if you believe this to be a mistake.");
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ban " + p.getName() + " ]=- Sentinel Anti-Grief -=[ You have been banned for attempting to use an NBT item. Contact an administrator if you believe this to be a mistake.");
logMessage = logMessage + "Banned: ✔\n";
banned = true;
} else {
logMessage = logMessage + "Banned: ✘\n";
banned = false;
}
logMessage = logMessage + "Denied: ✔";
denied = true;
Sentinel.log.info(logMessage);
notifyTrusted(p, i);
WebhookSender.sendEmbedWarning(p.getName(),i,denied,opRemoved,banned);
}
private static void notifyTrusted(Player p, String command) {
TextComponent message = new TextComponent(TextUtils.prefix(p.getName() + " has attempted a dangerous command!"));

View File

@@ -0,0 +1,35 @@
package io.github.thetrouper.sentinel.server.util;
public class GPTUtils {
// I'd be surprised if anyone knew how tf this shi works, I just asked GPT to write it.
public static double calculateSimilarity(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= len2; j++) {
dp[0][j] = j;
}
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j]));
}
}
}
int maxLen = Math.max(len1, len2);
int distance = dp[len1][len2];
double similarity = ((double) (maxLen - distance) / maxLen) * 100;
return similarity;
}
}

View File

@@ -5,6 +5,7 @@
package io.github.thetrouper.sentinel.server.util;
import io.github.thetrouper.sentinel.Sentinel;
import io.github.thetrouper.sentinel.commands.InfoCommand;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -17,9 +18,16 @@ import java.util.Set;
* Server utils
*/
public abstract class ServerUtils {
public static void sendDebugMessage(String message) {
if (InfoCommand.debugmode) {
Sentinel.log.info(message);
for (Player trustedPlayer : Bukkit.getOnlinePlayers()) {
if (Sentinel.isTrusted(trustedPlayer)) {
trustedPlayer.sendMessage(message);
}
}
}
}
/**
* List of names of online players
* @return list of names

View File

@@ -1,18 +1,27 @@
#
#
# Configure the plugin here, the default config may not be adequate to your needs.
#
#
config:
# Sentinel 0.0.2
# ____ __ ___
#/\ _`\ /\ \__ __ /\_ \
#\ \,\L\_\ __ ___\ \ ,_\/\_\ ___ __\//\ \
# \/_\__ \ /'__`\/' _ `\ \ \/\/\ \ /' _ `\ /'__`\\ \ \
# /\ \L\ \/\ __//\ \/\ \ \ \_\ \ \/\ \/\ \/\ __/ \_\ \_
# \ `\____\ \____\ \_\ \_\ \__\\ \_\ \_\ \_\ \____\/\____\
# \/_____/\/____/\/_/\/_/\/__/ \/_/\/_/\/_/\/____/\/____/
# ]======------ Configuration & Setup Guide ------=====[
# Sentinel is inspired by WickBot.com
# Be sure to check out their amazing discord bot!
config :
plugin:
# -------------------------------
# Important Setup (Do this first)
# -------------------------------
prefix: "§d§lSentinel §8» §7" # Prefix of the plugin. Line below is the discord webhook for logs to be sent to
webhook: "https://discord.com/api/webhooks/1124908469842096211/https://discord.com/api/webhooks/1124908469842096211/7NGOeFvtmxQ4n0_hSvbqhZUjnzRHIicLpHKETYU92n9JaLUPPsueBSn7w4wUfAnhjlLF"
webhook: "https://discord.com/api/webhooks/1124908469842096211/7NGOeFvtmxQ4n0_hSvbqhZUjnzRHIicLpHKETYU92n9JaLUPPsueBSn7w4wUfAnhjlLF"
trusted: # List the UUIDs of players who are trusted, will bypass the plugin and be immune to logs and are able to re-op themeselves
- "049460f7-21cb-42f5-8059-d42752bf406f" # obvWolf
block-specific: true # Defaulted true | Weather or not to block ALL plugin specific commands from non-trusted members (EX: minecraft:ban) these will not be logged.
prevent-nbt: true # Defaulted true | Should NBT items be blocked from the creative hotbar
log-nbt: true # Defaulted true | Should items and their NBT's be logged
prevent-cmdblocks: true # Defaulted true | Should all command block actions be blocked
prevent-cmdblocks: true # Defaulted true | Should all command block actions be blocked
log-cmdblocks: true # Defaulted true | Log attempts of command-block place-ery
dangerous: # These commands can only be run by "trusted" users
- "op"
@@ -27,4 +36,14 @@ config:
- "give"
deop: true # Defaulted true | This will remove an untrusted player's operator permissions whenever they attempt dangerous actions
ban: false # Defaulted false | This will ban a player when they attempt dangerous actions
reop-command: false # Defaulted false | This enables the command allowing trusted players to op themselves if they get deoped.
reop-command: false # Defaulted false | This enables the command allowing trusted players to op themselves if they get deoped.
# -------------------------------
# Chat Filter Setup & AntiSpam
# -------------------------------
chat:
# AntiSpam Heat system
anti-spam: true # Default true | Enables the anti-spam
default-gain: 1 # Heat gained as base for every message
medium-gain: 3 # Heat gained when your message is 50% similar
high-gain: 6 # Heat gained for
max-heat: 15 # Highest value of heat a player can reach