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:
TheTrouper
2023-10-17 17:01:12 -05:00
committed by obvWolf
parent 5ccaf5a99d
commit dd2a4e772f
6 changed files with 111 additions and 49 deletions

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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()];
}
}