New similarity calculator (Levenshtein distance) + fixes to the dictionary and chat notifications. Debug commands need work, and then testing for release can begin
This commit is contained in:
@@ -4,8 +4,12 @@
|
||||
|
||||
package io.github.thetrouper.sentinel.commands;
|
||||
|
||||
import io.github.thetrouper.sentinel.Sentinel;
|
||||
import io.github.thetrouper.sentinel.data.Config;
|
||||
import io.github.thetrouper.sentinel.server.functions.AntiSpam;
|
||||
import io.github.thetrouper.sentinel.server.functions.ProfanityFilter;
|
||||
import io.github.thetrouper.sentinel.server.util.Text;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -28,28 +32,53 @@ public class SentinelCommand extends CustomCommand {
|
||||
public void dispatchCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
Player p = (Player) sender;
|
||||
switch (args[0]) {
|
||||
case "debugmode" -> {
|
||||
debugmode = !debugmode;
|
||||
p.sendMessage(Text.prefix((debugmode ? "enabled" : "disabled") + " debug mode."));
|
||||
}
|
||||
case "testantiswear" -> {
|
||||
HashSet<Player> players = new HashSet<>();
|
||||
players.add((Player) sender);
|
||||
String msg = "";
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
msg = msg.concat(" " + args[i]);
|
||||
case "debug" -> {
|
||||
switch (args[1]) {
|
||||
case "testantiswear" -> {
|
||||
HashSet<Player> players = new HashSet<>();
|
||||
players.add((Player) sender);
|
||||
String msg = "";
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
msg = msg.concat(" " + args[i]);
|
||||
}
|
||||
msg = msg.trim();
|
||||
AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, (Player) sender, msg, players);
|
||||
ProfanityFilter.handleProfanityFilter(e);
|
||||
}
|
||||
case "testantispam" -> {
|
||||
HashSet<Player> players = new HashSet<>();
|
||||
players.add((Player) sender);
|
||||
String msg = "";
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
msg = msg.concat(" " + args[i]);
|
||||
}
|
||||
msg = msg.trim();
|
||||
AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, (Player) sender, msg, players);
|
||||
AntiSpam.handleAntiSpam(e);
|
||||
}
|
||||
case "testlang" -> {
|
||||
p.sendMessage(Sentinel.dict.get("exmaple-message"));
|
||||
}
|
||||
case "toggle" -> {
|
||||
debugmode = !debugmode;
|
||||
p.sendMessage(Text.prefix((debugmode ? "enabled" : "disabled") + " debug mode."));
|
||||
}
|
||||
}
|
||||
msg = msg.trim();
|
||||
AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, (Player) sender, msg, players);
|
||||
ProfanityFilter.handleProfanityFilter(e);
|
||||
}
|
||||
case "getHeat" -> {
|
||||
Player target = Bukkit.getPlayer(args[1]);
|
||||
if (target == null) {
|
||||
p.sendMessage(Text.prefix("Invalid Player!"));
|
||||
return;
|
||||
}
|
||||
p.sendMessage(Text.prefix("Heat of " + target.getName() + ": &8(&c" + AntiSpam.heatMap.get(target) + "&7/&4" + Config.punishHeat + "&8)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCompletions(CompletionBuilder builder) {
|
||||
builder.addCompletion(1,"debugmode");
|
||||
builder.addCompletion(1,"testantiswear");
|
||||
|
||||
builder.addCompletion(1,"debug");
|
||||
builder.addCompletion(1,"getHeat");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ public enum FAT {
|
||||
// I couldn't miss the opportunity to call the "Filter Action Type" FAT
|
||||
// Its rly just to make the tab completion of FilterAction easier
|
||||
BLOCK_SWEAR("Sentinel Profanity Filter",null,"swear-block-warn", "swear-block-notification", null,null),
|
||||
BLOCK_SPAM("Sentinel Anti-Spam", null, "spam-warning", "spam-notification",null,null),
|
||||
BLOCK_SPAM("Sentinel Anti-Spam", null, "spam-block-warn", "spam-notification",null,null),
|
||||
SWEAR("Sentinel Anti-Swear Log","Anti-Swear", "profanity-mute-warn", "profanity-mute-notification", Config.swearPunishCommand, Color.orange),
|
||||
SLUR("Sentinel Anti-Slur Log", "Anti-Slur", "slur-mute-warn", "slur-mute-notification", Config.strictPunishCommand, Color.red),
|
||||
SPAM("Sentinel Anti-Spam Log", "Anti-Spam", "spam-mute-warn", "spam-mute-notification", Config.spamPunishCommand, Color.pink);
|
||||
|
||||
@@ -35,13 +35,15 @@ public class FilterAction {
|
||||
fs.setRoundingMode(RoundingMode.DOWN);
|
||||
|
||||
TextComponent notif = new TextComponent();
|
||||
notif.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText((type != FAT.SPAM ? Sentinel.dict.get("severity-notification-hover").formatted(e.getMessage(), highlighted, severity) : Sentinel.dict.get("spam-notification-hover").formatted(e.getMessage(),lastMessageMap.get(offender),fs.format(similarity))))));
|
||||
notif.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText((type != FAT.SPAM && type != FAT.BLOCK_SPAM ? Sentinel.dict.get("severity-notification-hover").formatted(e.getMessage(), highlighted, severity) : Sentinel.dict.get("spam-notification-hover").formatted(e.getMessage(),lastMessageMap.get(offender),fs.format(similarity))))));
|
||||
notif.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sentinelcallback fpreport " + report));
|
||||
|
||||
warn.setText(Text.prefix(Sentinel.dict.get(type.getWarnTranslationKey())));
|
||||
offender.spigot().sendMessage(warn);
|
||||
|
||||
notif.setText(Text.prefix(Sentinel.dict.get(type.getNotifTranslationKey()).formatted(offender.getName(), scoreMap.get(offender), Config.punishScore)));
|
||||
String notiftext = Sentinel.dict.get(type.getNotifTranslationKey());
|
||||
|
||||
notif.setText(Text.prefix((type != FAT.SPAM && type != FAT.BLOCK_SPAM ? notiftext.formatted(offender.getName(), scoreMap.get(offender), Config.punishScore) : notiftext.formatted(offender.getName(),heatMap.get(offender),Config.punishHeat))));
|
||||
ServerUtils.forEachStaff(staffmember -> {
|
||||
staffmember.spigot().sendMessage(notif);
|
||||
});
|
||||
@@ -67,10 +69,10 @@ public class FilterAction {
|
||||
public static void sendConsoleLog(Player offender, AsyncPlayerChatEvent e, FAT type) {
|
||||
String log = "]=-" + type.getTitle() + "-=[\n" +
|
||||
"Player: " + offender.getName() +
|
||||
(type != FAT.BLOCK_SPAM ? "> Score: `" + scoreMap.get(offender) + "/" + Config.punishScore : "> Heat: `" + heatMap.get(offender) + "/" + Config.punishHeat) + "\n" +
|
||||
(type != FAT.BLOCK_SPAM && type != FAT.SPAM ? "> Score: `" + scoreMap.get(offender) + "/" + Config.punishScore : "> Heat: `" + heatMap.get(offender) + "/" + Config.punishHeat) + "\n" +
|
||||
"> UUID: " + offender.getUniqueId() + "\n" +
|
||||
(type != FAT.BLOCK_SPAM ? "Message: " + e.getMessage() : "Previous: " + lastMessageMap.get(offender)) + "\n" +
|
||||
(type != FAT.BLOCK_SPAM ? "Reduced: " + fullSimplify(e.getMessage()) : "Current: " + e.getMessage()) + "\n" +
|
||||
(type != FAT.BLOCK_SPAM && type != FAT.SPAM ? "Message: " + e.getMessage() : "Previous: " + lastMessageMap.get(offender)) + "\n" +
|
||||
(type != FAT.BLOCK_SPAM && type != FAT.SPAM ? "Reduced: " + fullSimplify(e.getMessage()) : "Current: " + e.getMessage()) + "\n" +
|
||||
(type.getExecutedCommand() != null ? "Executed: " + type.getExecutedCommand() : "Executed: Nothing, its a standard flag. You shouldn't be seeing this, please report it.");
|
||||
Sentinel.log.info(log);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
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.data.FAT;
|
||||
import io.github.thetrouper.sentinel.data.FilterAction;
|
||||
import io.github.thetrouper.sentinel.discord.WebhookSender;
|
||||
import io.github.thetrouper.sentinel.server.util.GPTUtils;
|
||||
import io.github.thetrouper.sentinel.server.util.ServerUtils;
|
||||
import io.github.thetrouper.sentinel.server.util.Text;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
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;
|
||||
@@ -31,27 +22,34 @@ public class AntiSpam {
|
||||
public static void handleAntiSpam(AsyncPlayerChatEvent e) {
|
||||
Player p = e.getPlayer();
|
||||
String message = Text.removeFirstColor(e.getMessage());
|
||||
Double sim = calculateSimilarity(e.getMessage(),lastMessageMap.get(p));
|
||||
if (!heatMap.containsKey(p)) heatMap.put(p, 0);
|
||||
|
||||
if (!lastMessageMap.containsKey(p)) {
|
||||
lastMessageMap.put(p,"/* Placeholder Message from Sentinel */");
|
||||
}
|
||||
if (!heatMap.containsKey(p)) {
|
||||
heatMap.put(p,0);
|
||||
}
|
||||
|
||||
if (lastMessageMap.containsKey(p)) {
|
||||
String lastMessage = lastMessageMap.get(p);
|
||||
double similarity = GPTUtils.calcSim(message, lastMessage);
|
||||
if (similarity > 0.25) heatMap.put(p, heatMap.get(p) + Config.lowGain);
|
||||
if (similarity > 0.5) heatMap.put(p, heatMap.get(p) + Config.mediumGain);
|
||||
if (similarity > 0.9) heatMap.put(p, heatMap.get(p) + Config.highGain);
|
||||
}
|
||||
|
||||
if (heatMap.get(p) > Config.punishHeat) {
|
||||
e.setCancelled(true);
|
||||
FilterAction.filterAction(p,e,null,null, sim, FAT.SPAM);
|
||||
FilterAction.filterAction(p,e,null,null, GPTUtils.calcSim(e.getMessage(),lastMessageMap.get(p)), FAT.SPAM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (heatMap.get(p) > Config.blockHeat) {
|
||||
e.setCancelled(true);
|
||||
FilterAction.filterAction(p,e,null,null, sim, FAT.BLOCK_SPAM);
|
||||
FilterAction.filterAction(p,e,null,null, GPTUtils.calcSim(e.getMessage(),lastMessageMap.get(p)), FAT.BLOCK_SPAM);
|
||||
heatMap.put(p, heatMap.get(p) + Config.highGain);
|
||||
return;
|
||||
}
|
||||
if (lastMessageMap.containsKey(p)) {
|
||||
String lastMessage = lastMessageMap.get(p);
|
||||
double similarity = calculateSimilarity(message, lastMessage);
|
||||
if (similarity > 0.25) heatMap.put(p, heatMap.get(p) + Config.lowGain);
|
||||
if (similarity > 0.5) heatMap.put(p, heatMap.get(p) + Config.mediumGain);
|
||||
if (similarity > 0.9) heatMap.put(p, heatMap.get(p) + Config.highGain);
|
||||
}
|
||||
lastMessageMap.put(p, message);
|
||||
}
|
||||
public static void decayHeat() {
|
||||
|
||||
@@ -32,4 +32,37 @@ public class GPTUtils {
|
||||
double similarity = ((double) (maxLen - distance) / maxLen) * 100;
|
||||
return similarity;
|
||||
}
|
||||
|
||||
public static double calcSim(String s1, String s2) {
|
||||
int maxLength = Math.max(s1.length(), s2.length());
|
||||
if (maxLength == 0) {
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
int distance = calculateLevenshteinDistance(s1, s2);
|
||||
double similarity = ((double) (maxLength - distance) / maxLength) * 100.0;
|
||||
|
||||
return similarity;
|
||||
}
|
||||
|
||||
public static int calculateLevenshteinDistance(String s1, String s2) {
|
||||
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
|
||||
|
||||
for (int i = 0; i <= s1.length(); i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
|
||||
for (int j = 0; j <= s2.length(); j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= s1.length(); i++) {
|
||||
for (int j = 1; j <= s2.length(); j++) {
|
||||
int cost = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? 0 : 1;
|
||||
dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost);
|
||||
}
|
||||
}
|
||||
|
||||
return dp[s1.length()][s2.length()];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user