Chat Click Callback and false positive reporting
This commit is contained in:
@@ -110,6 +110,7 @@ public final class Sentinel extends JavaPlugin {
|
||||
new ReplyCommand().register();
|
||||
new ReopCommand().register();
|
||||
new SocialSpyCommand().register();
|
||||
new ChatClickCallback().register();
|
||||
|
||||
// Events
|
||||
manager.registerEvents(new CommandEvent(),this);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* This file is for tutorial purposes made by ImproperIssues. Distribute if you want :)
|
||||
*/
|
||||
|
||||
package io.github.thetrouper.sentinel.commands;
|
||||
|
||||
import io.github.thetrouper.sentinel.server.functions.ProfanityFilter;
|
||||
import io.github.thetrouper.sentinel.server.functions.ReportFalsePositives;
|
||||
import io.github.thetrouper.sentinel.server.util.Cooldown;
|
||||
import io.github.thetrouper.sentinel.server.util.TextUtils;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Example command
|
||||
*/
|
||||
public class ChatClickCallback extends CustomCommand {
|
||||
public static Cooldown<UUID> fpReportCooldown;
|
||||
public ChatClickCallback() {
|
||||
super("sentinelcallback");
|
||||
this.setPrintStacktrace(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
Player p = (Player) sender;
|
||||
switch (args[0]) {
|
||||
case "fpreport" -> {
|
||||
if (fpReportCooldown.isOnCooldown(p.getUniqueId()) && !p.isOp()) {
|
||||
p.sendMessage(TextUtils.prefix("This action is on cooldown! " + fpReportCooldown.getCooldown(p.getUniqueId())));
|
||||
} else {
|
||||
ReportFalsePositives.sendFalsePositiveReport(args[1]);
|
||||
p.sendMessage(TextUtils.prefix("Successfully reported a false positive!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCompletions(CompletionBuilder builder) {
|
||||
builder.addCompletion(1,"you");
|
||||
builder.addCompletion(1,"must");
|
||||
builder.addCompletion(1,"be");
|
||||
builder.addCompletion(1,"called");
|
||||
builder.addCompletion(1,"before");
|
||||
builder.addCompletion(1,"running");
|
||||
builder.addCompletion(1,"a");
|
||||
builder.addCompletion(1,"callback");
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
package io.github.thetrouper.sentinel.commands;
|
||||
|
||||
import io.github.thetrouper.sentinel.server.functions.ProfanityFilter;
|
||||
import io.github.thetrouper.sentinel.server.functions.ReportFalsePositives;
|
||||
import io.github.thetrouper.sentinel.server.util.TextUtils;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -12,6 +13,8 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Example command
|
||||
|
||||
@@ -41,31 +41,31 @@ 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);
|
||||
blockSwear(p,highlightProfanity(message),message,severity);
|
||||
blockSwear(p,highlightProfanity(message),message,severity,e);
|
||||
}
|
||||
case "medium-low" -> {
|
||||
ServerUtils.sendDebugMessage("AntiSwear Flag, Message: " + message + " Concentrated: " + fullSimplify(message) + " Severity: " + severity + " Previous Score: " + scoreMap.get(p) +" Adding Score: " + Config.mediumLowScore);
|
||||
scoreMap.put(p, scoreMap.get(p) + Config.mediumLowScore);
|
||||
e.setCancelled(true);
|
||||
blockSwear(p,highlightProfanity(message),message,severity);
|
||||
blockSwear(p,highlightProfanity(message),message,severity,e);
|
||||
}
|
||||
case "medium" -> {
|
||||
ServerUtils.sendDebugMessage("AntiSwear Flag, Message: " + message + " Concentrated: " + fullSimplify(message) + " Severity: " + severity + " Previous Score: " + scoreMap.get(p) +" Adding Score: " + Config.mediumScore);
|
||||
scoreMap.put(p, scoreMap.get(p) + Config.mediumScore);
|
||||
e.setCancelled(true);
|
||||
blockSwear(p,highlightProfanity(message),message,severity);
|
||||
blockSwear(p,highlightProfanity(message),message,severity,e);
|
||||
}
|
||||
case "medium-high" -> {
|
||||
ServerUtils.sendDebugMessage("AntiSwear Flag, Message: " + message + " Concentrated: " + fullSimplify(message) + " Severity: " + severity + " Previous Score: " + scoreMap.get(p) +" Adding Score: " + Config.mediumHighScore);
|
||||
scoreMap.put(p, scoreMap.get(p) + Config.mediumHighScore);
|
||||
e.setCancelled(true);
|
||||
blockSwear(p,highlightProfanity(message),message,severity);
|
||||
blockSwear(p,highlightProfanity(message),message,severity,e);
|
||||
}
|
||||
case "high" -> {
|
||||
ServerUtils.sendDebugMessage("AntiSwear Flag, Message: " + message + " Concentrated: " + fullSimplify(message) + " Severity: " + severity + " Previous Score: " + scoreMap.get(p) +" Adding Score: " + Config.highScore);
|
||||
scoreMap.put(p, scoreMap.get(p) + Config.highScore);
|
||||
e.setCancelled(true);
|
||||
blockSwear(p,highlightProfanity(message),message,severity);
|
||||
blockSwear(p,highlightProfanity(message),message,severity,e);
|
||||
}
|
||||
case "slur" -> {
|
||||
// Insta-Punish
|
||||
@@ -107,14 +107,14 @@ public class ProfanityFilter {
|
||||
});
|
||||
if (Config.logSwear) WebhookSender.sendSlurLog(player,origMessage,scoreMap.get(player));
|
||||
}
|
||||
public static void blockSwear(Player player, String highlightedMSG, String origMessage, String severity) {
|
||||
public static void blockSwear(Player player, String highlightedMSG, String origMessage, String severity, AsyncPlayerChatEvent e) {
|
||||
player.sendMessage(TextUtils.prefix(("§cPlease do not swear in chat! Attempting to bypass this filter will result in a mute!")));
|
||||
String hover = ("§bOriginal: §f" + origMessage + "\n§bSanitized: §f" + highlightedMSG + "\n§bSeverity: §c" + severity + "\n§7§o(click to copy)");
|
||||
String hover = ("§bOriginal: §f" + origMessage + "\n§bSanitized: §f" + highlightedMSG + "\n§bSeverity: §c" + severity + "\n§7§o(click to report false positive)");
|
||||
TextComponent text = new TextComponent();
|
||||
text.setText(TextUtils.prefix(
|
||||
("§b§n" + player.getName() + "§7 has triggered the anti-swear! §8(§c" + scoreMap.get(player) + "§7/§4" + Config.punishScore + "§8)")));
|
||||
text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(hover)));
|
||||
text.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, origMessage));
|
||||
text.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "sentinelcallback fpreport " + ReportFalsePositives.generateReport(e)));
|
||||
|
||||
ServerUtils.forEachStaff(staff -> {
|
||||
staff.spigot().sendMessage(text);
|
||||
@@ -221,13 +221,10 @@ public class ProfanityFilter {
|
||||
return "safe";
|
||||
}
|
||||
|
||||
private static String removeFalsePositives(String text) {
|
||||
for (String falsePositive : swearWhitelist) {
|
||||
text = text.replace(falsePositive, "");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static boolean ContainsProfanity(String text) {
|
||||
return containsSwears(text) || containsSlurs(text);
|
||||
}
|
||||
private static boolean containsSwears(String text) {
|
||||
ServerUtils.sendDebugMessage("Debug: [AntiSwear] Checking for swears: " + swearBlacklist.toString());
|
||||
for (String swear : swearBlacklist) {
|
||||
@@ -242,23 +239,28 @@ public class ProfanityFilter {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String convertLeetSpeakCharacters(String text) {
|
||||
public static String removeFalsePositives(String text) {
|
||||
for (String falsePositive : swearWhitelist) {
|
||||
text = text.replace(falsePositive, "");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
public static String convertLeetSpeakCharacters(String text) {
|
||||
text = TextUtils.fromLeetString(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private static String stripSpecialCharacters(String text) {
|
||||
public static String stripSpecialCharacters(String text) {
|
||||
text = text.replaceAll("[^A-Za-z0-9.,!?;:'\"()\\[\\]{}]", "").trim();
|
||||
return text;
|
||||
}
|
||||
|
||||
private static String simplifyRepeatingLetters(String text) {
|
||||
public static String simplifyRepeatingLetters(String text) {
|
||||
text = TextUtils.replaceRepeatingLetters(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
private static String removePeriodsAndSpaces(String text) {
|
||||
public static String removePeriodsAndSpaces(String text) {
|
||||
return text.replaceAll("[^A-Za-z0-9]", "").replace(" ", "");
|
||||
}
|
||||
public static void decayScore() {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
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.Emojis;
|
||||
import io.github.thetrouper.sentinel.discord.DiscordWebhook;
|
||||
import io.github.thetrouper.sentinel.server.util.Randomizer;
|
||||
import io.github.thetrouper.sentinel.server.util.ServerUtils;
|
||||
import io.github.thetrouper.sentinel.server.util.TextUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReportFalsePositives {
|
||||
public static Map<String,AsyncPlayerChatEvent> reportMap;
|
||||
public static String generateReport(AsyncPlayerChatEvent e) {
|
||||
final long reportLong = Randomizer.generateID();
|
||||
final String reportID = Long.toString(reportLong);
|
||||
ServerUtils.sendDebugMessage(TextUtils.prefix("DEBUG: Generating chat filter report"));
|
||||
reportMap.put(reportID,e);
|
||||
ServerUtils.sendDebugMessage(TextUtils.prefix("DEBUG: Generated report. ID:" + reportID + " Message: \"" + reportMap.get(reportID).getMessage() + "\" Expires in 60 seconds"));
|
||||
Bukkit.getScheduler().runTaskLater(Sentinel.getInstance(),()->{
|
||||
reportMap.remove(reportID);
|
||||
ServerUtils.sendDebugMessage(TextUtils.prefix("DEBUG: A report expired. ID: " + reportID));
|
||||
},60000);
|
||||
return reportID;
|
||||
}
|
||||
public static void sendFalsePositiveReport(String reportID) {
|
||||
AsyncPlayerChatEvent e = reportMap.get(reportID);
|
||||
String orig = e.getMessage();
|
||||
|
||||
String lowercasedText = orig.toLowerCase();
|
||||
String remFP = ProfanityFilter.removeFalsePositives(lowercasedText);
|
||||
String convertedLeet = ProfanityFilter.convertLeetSpeakCharacters(remFP);
|
||||
String remSpecials = ProfanityFilter.stripSpecialCharacters(convertedLeet);
|
||||
String simplifyRep = ProfanityFilter.simplifyRepeatingLetters(remSpecials);
|
||||
String sanitized = ProfanityFilter. removePeriodsAndSpaces(simplifyRep);
|
||||
|
||||
sendEmbed(e.getPlayer(),orig,lowercasedText,remFP,convertedLeet,remSpecials,simplifyRep,sanitized);
|
||||
|
||||
}
|
||||
|
||||
public static void sendEmbed(Player player,
|
||||
String message,
|
||||
String lowercased,
|
||||
String remFP,
|
||||
String convertedLeet,
|
||||
String remSpecials,
|
||||
String simplifyRep,
|
||||
String sanitized) {
|
||||
ServerUtils.sendDebugMessage("Creating FalsePositive Webhook...");
|
||||
DiscordWebhook webhook = new DiscordWebhook(Config.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-Swear False Positive","","")
|
||||
.setTitle("Flag Report:")
|
||||
.setDescription(
|
||||
Emojis.rightSort + "Player: " + player.getName() + " " + Emojis.target + "\\n" +
|
||||
Emojis.space + Emojis.arrowRight + "UUID: `" + player.getUniqueId() + "`\\n"
|
||||
)
|
||||
.addField("Original Message", "`" + message + "` " + (ProfanityFilter.ContainsProfanity(message) ? Emojis.alarm : ""), false)
|
||||
.addField("Lowercase", "`" + lowercased + "` " + (ProfanityFilter.ContainsProfanity(lowercased) ? Emojis.alarm : ""), false)
|
||||
.addField("Removed FPs", "`" + remFP + "` " + (ProfanityFilter.ContainsProfanity(remFP) ? Emojis.alarm : ""), false)
|
||||
.addField("Converted Leet", "`" + convertedLeet + "` " + (ProfanityFilter.ContainsProfanity(convertedLeet) ? Emojis.alarm : ""), false)
|
||||
.addField("Removed Specials", "`" + remSpecials + "` " + (ProfanityFilter.ContainsProfanity(remSpecials) ? Emojis.alarm : ""), false)
|
||||
.addField("Simplify Repeats", "`" + simplifyRep + "` " + (ProfanityFilter.ContainsProfanity(simplifyRep) ? Emojis.alarm : ""), false)
|
||||
.addField("Fully Sanitized Message", ProfanityFilter.highlightProfanity(sanitized,"`", "`") + " " + Emojis.noDM, false)
|
||||
.setColor(Color.green)
|
||||
.setThumbnail("https://crafatar.com/avatars/" + player.getUniqueId() + "?size=64&&overlay");
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.github.thetrouper.sentinel.server.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Cooldown<T> {
|
||||
|
||||
private final Map<T,Long> timer;
|
||||
|
||||
public Cooldown() {
|
||||
this.timer = new HashMap<>();
|
||||
}
|
||||
|
||||
private <O> O getOrDefault(O value, O def) {
|
||||
return value != null ? value : def;
|
||||
}
|
||||
|
||||
public long getCooldown(T obj) {
|
||||
return Math.max(getOrDefault(timer.get(obj), 0L) - System.currentTimeMillis(), 0L);
|
||||
}
|
||||
|
||||
public double getCooldownSec(T obj) {
|
||||
final long cooldown = this.getCooldown(obj);
|
||||
return MathUtils.round(cooldown / 1000.0, 100);
|
||||
}
|
||||
|
||||
public boolean isOnCooldown(T obj) {
|
||||
return getCooldown(obj) > 0L;
|
||||
}
|
||||
|
||||
public void setCooldown(T obj, long millis) {
|
||||
timer.put(obj, System.currentTimeMillis() + millis);
|
||||
}
|
||||
|
||||
public void addCooldown(T obj, long millis) {
|
||||
setCooldown(obj, getCooldown(obj) + millis);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.github.thetrouper.sentinel.server.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class MathUtils {
|
||||
|
||||
public static double avg(Integer... ints) {
|
||||
final List<Integer> list = Arrays.stream(ints).filter(Objects::nonNull).toList();
|
||||
return avg(list);
|
||||
}
|
||||
|
||||
public static double avg(List<Integer> ints) {
|
||||
double sum = 0.0;
|
||||
for (Integer i : ints) sum += i;
|
||||
return sum / ints.size();
|
||||
}
|
||||
|
||||
public static double round(double value, int nthPlace) {
|
||||
return Math.floor(value * nthPlace) / nthPlace;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user