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;
|
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.functions.ProfanityFilter;
|
||||||
import io.github.thetrouper.sentinel.server.util.Text;
|
import io.github.thetrouper.sentinel.server.util.Text;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
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) {
|
public void dispatchCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
Player p = (Player) sender;
|
Player p = (Player) sender;
|
||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case "debugmode" -> {
|
case "debug" -> {
|
||||||
debugmode = !debugmode;
|
switch (args[1]) {
|
||||||
p.sendMessage(Text.prefix((debugmode ? "enabled" : "disabled") + " debug mode."));
|
case "testantiswear" -> {
|
||||||
}
|
HashSet<Player> players = new HashSet<>();
|
||||||
case "testantiswear" -> {
|
players.add((Player) sender);
|
||||||
HashSet<Player> players = new HashSet<>();
|
String msg = "";
|
||||||
players.add((Player) sender);
|
for (int i = 1; i < args.length; i++) {
|
||||||
String msg = "";
|
msg = msg.concat(" " + args[i]);
|
||||||
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);
|
case "getHeat" -> {
|
||||||
ProfanityFilter.handleProfanityFilter(e);
|
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
|
@Override
|
||||||
public void registerCompletions(CompletionBuilder builder) {
|
public void registerCompletions(CompletionBuilder builder) {
|
||||||
builder.addCompletion(1,"debugmode");
|
builder.addCompletion(1,"debug");
|
||||||
builder.addCompletion(1,"testantiswear");
|
builder.addCompletion(1,"getHeat");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public enum FAT {
|
|||||||
// I couldn't miss the opportunity to call the "Filter Action Type" 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
|
// 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_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),
|
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),
|
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);
|
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);
|
fs.setRoundingMode(RoundingMode.DOWN);
|
||||||
|
|
||||||
TextComponent notif = new TextComponent();
|
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));
|
notif.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sentinelcallback fpreport " + report));
|
||||||
|
|
||||||
warn.setText(Text.prefix(Sentinel.dict.get(type.getWarnTranslationKey())));
|
warn.setText(Text.prefix(Sentinel.dict.get(type.getWarnTranslationKey())));
|
||||||
offender.spigot().sendMessage(warn);
|
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 -> {
|
ServerUtils.forEachStaff(staffmember -> {
|
||||||
staffmember.spigot().sendMessage(notif);
|
staffmember.spigot().sendMessage(notif);
|
||||||
});
|
});
|
||||||
@@ -67,10 +69,10 @@ public class FilterAction {
|
|||||||
public static void sendConsoleLog(Player offender, AsyncPlayerChatEvent e, FAT type) {
|
public static void sendConsoleLog(Player offender, AsyncPlayerChatEvent e, FAT type) {
|
||||||
String log = "]=-" + type.getTitle() + "-=[\n" +
|
String log = "]=-" + type.getTitle() + "-=[\n" +
|
||||||
"Player: " + offender.getName() +
|
"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" +
|
"> UUID: " + offender.getUniqueId() + "\n" +
|
||||||
(type != FAT.BLOCK_SPAM ? "Message: " + e.getMessage() : "Previous: " + lastMessageMap.get(offender)) + "\n" +
|
(type != FAT.BLOCK_SPAM && type != FAT.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 ? "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.");
|
(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);
|
Sentinel.log.info(log);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,16 @@
|
|||||||
package io.github.thetrouper.sentinel.server.functions;
|
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.Config;
|
||||||
import io.github.thetrouper.sentinel.data.FAT;
|
import io.github.thetrouper.sentinel.data.FAT;
|
||||||
import io.github.thetrouper.sentinel.data.FilterAction;
|
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.GPTUtils;
|
||||||
import io.github.thetrouper.sentinel.server.util.ServerUtils;
|
|
||||||
import io.github.thetrouper.sentinel.server.util.Text;
|
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.entity.Player;
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static io.github.thetrouper.sentinel.server.util.GPTUtils.calculateSimilarity;
|
|
||||||
|
|
||||||
public class AntiSpam {
|
public class AntiSpam {
|
||||||
public static Map<Player, Integer> heatMap;
|
public static Map<Player, Integer> heatMap;
|
||||||
public static Map<Player, String> lastMessageMap;
|
public static Map<Player, String> lastMessageMap;
|
||||||
@@ -31,27 +22,34 @@ public class AntiSpam {
|
|||||||
public static void handleAntiSpam(AsyncPlayerChatEvent e) {
|
public static void handleAntiSpam(AsyncPlayerChatEvent e) {
|
||||||
Player p = e.getPlayer();
|
Player p = e.getPlayer();
|
||||||
String message = Text.removeFirstColor(e.getMessage());
|
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) {
|
if (heatMap.get(p) > Config.punishHeat) {
|
||||||
e.setCancelled(true);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heatMap.get(p) > Config.blockHeat) {
|
if (heatMap.get(p) > Config.blockHeat) {
|
||||||
e.setCancelled(true);
|
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);
|
heatMap.put(p, heatMap.get(p) + Config.highGain);
|
||||||
return;
|
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);
|
lastMessageMap.put(p, message);
|
||||||
}
|
}
|
||||||
public static void decayHeat() {
|
public static void decayHeat() {
|
||||||
|
|||||||
@@ -32,4 +32,37 @@ public class GPTUtils {
|
|||||||
double similarity = ((double) (maxLen - distance) / maxLen) * 100;
|
double similarity = ((double) (maxLen - distance) / maxLen) * 100;
|
||||||
return similarity;
|
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()];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,25 +14,25 @@
|
|||||||
"no-user-reply" : "§cYou have nobody to reply to!",
|
"no-user-reply" : "§cYou have nobody to reply to!",
|
||||||
"spy-enabled" : "SocialSpy is now enabled.",
|
"spy-enabled" : "SocialSpy is now enabled.",
|
||||||
"spy-disabled" : "SocialSpy is now disabled.",
|
"spy-disabled" : "SocialSpy is now disabled.",
|
||||||
"spam-warning" : "Do not spam in chat! Please wait before sending another message.",
|
|
||||||
"action-automatic" : "§7This action was preformed automatically\n§7by the §bSentinel Anti-Spam§7 algorithm.",
|
"action-automatic" : "§7This action was preformed automatically\n§7by the §bSentinel Anti-Spam§7 algorithm.",
|
||||||
"spam-notification" : "§b§n%1$s§7 might be spamming! §8(§c%2$s§7/§4%3$s§8)",
|
"action-automatic-reportable" : "§7This action was preformed automatically \n§7by the §bSentinel Profanity Filter§7 algorithm!\n§8§o(Click to report false positive)",
|
||||||
"spam-notification-hover" : "§8]==-- §d§lSentinel §8--==[\n§bPrevious: §f%1$s\n§bCurrent: §f%2$s\n§bSimilarity §f%3$s",
|
|
||||||
"spam-punished" : "§cYou have been auto-punished for violating the anti-spam repetitively!",
|
|
||||||
"spam-punish-notification" : "§b§n%1$s§7 has been auto-muted by the anti spam! §8(§c%2$s§7/§4%3$s§8)",
|
|
||||||
"unicode-warn" : "§cDo not send non standard unicode in chat!",
|
"unicode-warn" : "§cDo not send non standard unicode in chat!",
|
||||||
"message-sent" : "§d§lMessage §8» §b[§fYou §e>§f %1$s§b] §7%2$s",
|
"message-sent" : "§d§lMessage §8» §b[§fYou §e>§f %1$s§b] §7%2$s",
|
||||||
"message-received" : "§d§lMessage §8» §b[§f%1$s §e>§f You§b] §7%2$s",
|
"message-received" : "§d§lMessage §8» §b[§f%1$s §e>§f You§b] §7%2$s",
|
||||||
"spy-message" : "§d§lSpy §8» §b§n%1$s§7 has messaged §b§n%2$s§7.",
|
"spy-message" : "§d§lSpy §8» §b§n%1$s§7 has messaged §b§n%2$s§7.",
|
||||||
"spy-message-hover" : "§8]==-- §d§lSocialSpy §8--==[\n§bSender: §f%1$S\n§bReceiver: §f%2$S\n§bMessage: §f%3$S",
|
"spy-message-hover" : "§8]==-- §d§lSocialSpy §8--==[\n§bSender: §f%1$S\n§bReceiver: §f%2$S\n§bMessage: §f%3$S",
|
||||||
"action-automatic-reportable" : "§7This action was preformed automatically \n§7by the §bSentinel Profanity Filter§7 algorithm!\n§8§o(Click to report false positive)",
|
|
||||||
"profanity-mute-warn" : "You have been auto muted for repeated violation of the profanity filter! §7§o(Hover for more info)",
|
"profanity-mute-warn" : "You have been auto muted for repeated violation of the profanity filter! §7§o(Hover for more info)",
|
||||||
"profanity-mute-notification" : "§b§n%1$s§7 has been auto-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
"profanity-mute-notification" : "§b§n%1$s§7 has been auto-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
||||||
"slur-mute-warn" : "§cYou have been insta-punished by the anti-slur! §7§o(Hover for more info)",
|
"slur-mute-warn" : "§cYou have been insta-punished by the anti-slur! §7§o(Hover for more info)",
|
||||||
"slur-mute-notification" : "§b§n%1$s§7 has been insta-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
"slur-mute-notification" : "§b§n%1$s§7 has been insta-muted by the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
||||||
"swear-block-warn" : "§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute! §7§o(Hover for more info)",
|
"swear-block-warn" : "§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute! §7§o(Hover for more info)",
|
||||||
"swear-block-notification" : "§b§n%1$s§7 has triggered the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
"swear-block-notification" : "§b§n%1$s§7 has triggered the anti-swear! §8(§c%2$s§7/§4%3$s§8)",
|
||||||
|
"spam-notification" : "§b§n%1$s§7 might be spamming! §8(§c%2$s§7/§4%3$s§8)",
|
||||||
|
"spam-notification-hover" : "§8]==-- §d§lSentinel §8--==[\n§bPrevious: §f%1$s\n§bCurrent: §f%2$s\n§bSimilarity §f%3$s",
|
||||||
|
"spam-block-warn" : "Do not spam in chat! Please wait before sending another message.",
|
||||||
|
"spam-mute-warn" : "§cYou have been auto-punished for violating the anti-spam repetitively!",
|
||||||
|
"spam-mute-notification" : "§b§n%1$s§7 has been auto-muted by the anti spam! §8(§c%2$s§7/§4%3$s§8)",
|
||||||
"filter-notification-hover" : "§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§8§o(Click to report false positive)",
|
"filter-notification-hover" : "§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§8§o(Click to report false positive)",
|
||||||
"severity-notification-hover" : "§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§bSeverity: §c%3$s\n§7§o(click to report false positive)",
|
"severity-notification-hover" : "§bOriginal: §f%1$s\n§bSanitized: §f%2$s\n§bSeverity: §c%3$s\n§7§o(click to report false positive)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user