7 Commits
Author SHA1 Message Date
auto de24ca4437 Refactor PlayerListener for mute handling and text prioritization
Improved the PlayerListener class by adjusting event handling priority and adding nullability constraints for PlayerDeathEvent for better program robustness. Also removed a now unused import of the TextComponent class for cleaner codebase management.
2024-04-13 16:15:35 +02:00
auto e351e86bea Add mute handling and text styling in PlayerListener
Enhanced PlayerListener feature by incorporating checks for server mute status before sending death messages. Additionally, styled the death messages by adding text color and italics when mute state is false to improve readability.
2024-04-10 22:02:01 +02:00
auto 4e92285261 Add throwable to warn and error methods in ALogger
This update introduces modifications to the 'ALogger.java' file to handle possible exceptions more efficiently. It extends the warn and error functions to include a Throwable parameter, making error logs more informative for easier debugging.
2024-04-06 15:35:08 +02:00
auto a377bdfe48 Implement player death message limits
The code now includes player death message limits, with a configured maximum number of messages per defined period. This was added in the PlayerListener class and configuration options were placed in Config.java. This change will restrict spamming of death messages.
2024-04-06 13:11:05 +02:00
auto 4af85d0e79 Add option to disable chat filters in private channels
A constructor parameter and corresponding field 'disableInPrivate' have been added to the ChatFilter class. This handles disabling specific chat filters in private channels. The necessary checks have been implemented in RegexManager. The configuration for each filter is also updated in RegexConfig to incorporate this new functionality.
2024-03-31 13:37:05 +02:00
auto 034de90062 Implement evidence for automatic ban for 'punish' filter violations
When a user who violate the 'punish' filter rules is automatically banned, there will now be a post about it in the #evidence channel.
2024-03-02 20:41:59 +01:00
auto bd8fa02f1e Add 'punish' filter and automatic banning functionality
Extended the RegexManager filterText method to include a 'punish' case that triggers an automatic ban for users who violate the filter. This commit also updates the PluginMessageListener to handle 'punish' commands, thus completing the execution of an auto-ban function.
2024-03-02 19:18:13 +01:00
9 changed files with 130 additions and 9 deletions
@@ -543,4 +543,12 @@ public final class Config {
NICK_ALLOWED_COLOR_CODESLIST = getList("nicknames.allowed-color-codes", List.of("&0", "&1", "&2", "&3", "&4", "&5", "&6", "&7", "&8", "&9", "&a", "&b", "&c", "&d", "&e", "&f", "&r"));
NICK_CURRENT = getString("nicknames.messages.nick-current", NICK_CURRENT);
}
public static int DEATH_MESSAGES_MAX_PER_PERIOD = 5;
public static int DEATH_MESSAGES_LIMIT_PERIOD_MINUTES = 15;
private static void deathMessagesSettings() {
DEATH_MESSAGES_MAX_PER_PERIOD = getInt("death-messages.max-per-period", DEATH_MESSAGES_MAX_PER_PERIOD);
DEATH_MESSAGES_LIMIT_PERIOD_MINUTES = getInt("death-messages.limit-period-minutes", DEATH_MESSAGES_LIMIT_PERIOD_MINUTES);
}
}
@@ -178,13 +178,18 @@ public final class RegexConfig {
String regex = entry.getValue().node("regex").getString();
String replacement = entry.getValue().node("replacement").getString();
List<String> exclusions = entry.getValue().node("exclusions").getList(io.leangen.geantyref.TypeToken.get(String.class), new ArrayList<>());
boolean disableInPrivate = false;
ConfigurationNode node = entry.getValue().node("disable-in-private");
if (node != null) {
disableInPrivate = node.getBoolean();
}
if (type == null || type.isEmpty() || regex == null || regex.isEmpty()) {
ALogger.warn("Filter: " + name + " was set up incorrectly");
} else {
if (replacement == null || replacement.isEmpty()) {
replacement = name;
}
ChatFilter chatFilter = new ChatFilter(name, type, regex, replacement, exclusions);
ChatFilter chatFilter = new ChatFilter(name, type, regex, replacement, exclusions, disableInPrivate);
RegexManager.addFilter(chatFilter);
}
} catch(SerializationException ex) {
@@ -8,10 +8,13 @@ import com.alttd.chat.objects.ModifiableString;
import com.alttd.chat.util.ALogger;
import net.luckperms.api.cacheddata.CachedPermissionData;
import net.luckperms.api.model.user.User;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.regex.Pattern;
public class RegexManager {
@@ -43,17 +46,24 @@ public class RegexManager {
}
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, String channel) { // TODO loop all objects in the list and check if they violate based on the MATCHER
return filterText(playerName, uuid, modifiableString, true, channel);
return filterText(playerName, uuid, modifiableString, true, channel, null);
}
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, boolean matcher, String channel) {
return filterText(playerName, uuid, modifiableString, matcher, channel, null);
}
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, boolean matcher, String channel, Consumer<FilterType> filterAction) {
User user = ChatAPI.get().getLuckPerms().getUserManager().getUser(uuid);
if (user == null) {
ALogger.warn("Tried to check chat filters for a user who doesn't exist in LuckPerms");
return false;
}
CachedPermissionData permissionData = user.getCachedData().getPermissionData();
boolean isPrivate = channel.equals("party");
for(ChatFilter chatFilter : chatFilters) {
if (isPrivate && chatFilter.isDisabledInPrivate())
continue;
switch (chatFilter.getType()) {
case CHAT:
break;
@@ -74,6 +84,25 @@ public class RegexManager {
chatFilter.replaceMatcher(modifiableString);
}
break;
case PUNISH:
if (permissionData.checkPermission("chat.bypass-punish").asBoolean())
break;
if (chatFilter.matches(modifiableString)) {
ALogger.info(playerName + " triggered the punish filter for " + chatFilter.getName()
+ " with: " + modifiableString.string() + ".");
if (filterAction == null){
ALogger.info("No filterAction was provided, not doing anything");
return false;
}
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
ALogger.warn("Tried to punish a player who triggered the filter, but the player is offline.");
return false;
}
filterAction.accept(FilterType.PUNISH);
return false;
}
}
}
return true;
@@ -17,14 +17,16 @@ public class ChatFilter {
private final Pattern pattern;
private final String replacement;
private final List<String> exclusions;
private final boolean disableInPrivate;
public ChatFilter(String name, String type, String regex, String replacement, List<String> exclusions) {
public ChatFilter(String name, String type, String regex, String replacement, List<String> exclusions, boolean disableInPrivate) {
this.name = name;
this.filterType = FilterType.getType(type);
this.regex = regex;
this.pattern = Pattern.compile(getRegex(), Pattern.CASE_INSENSITIVE);
this.replacement = replacement;
this.exclusions = exclusions;
this.disableInPrivate = disableInPrivate;
}
public String getName() {
@@ -47,6 +49,10 @@ public class ChatFilter {
return this.exclusions;
}
public boolean isDisabledInPrivate() {
return disableInPrivate;
}
public boolean matches(ModifiableString filterableString) {
String input = filterableString.string();
Matcher matcher = pattern.matcher(input);
@@ -5,7 +5,8 @@ public enum FilterType {
EMOTE("emote"),
CHAT("chat"),
REPLACEMATCHER("replacematcher"),
BLOCK("block");
BLOCK("block"),
PUNISH("punish");
private final String name;
@@ -16,6 +16,10 @@ public class ALogger {
logger.warn(message);
}
public static void warn(String message, Throwable throwable) {
logger.warn(message, throwable);
}
public static void info(String message) {
logger.info(message);
}
@@ -23,4 +27,8 @@ public class ALogger {
public static void error(String message) {
logger.error(message);
}
public static void error(String message, Throwable throwable) {
logger.error(message, throwable);
}
}
@@ -5,13 +5,12 @@ import com.alttd.chat.config.Config;
import com.alttd.chat.handler.ChatHandler;
import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.managers.RegexManager;
import com.alttd.chat.objects.ChatFilter;
import com.alttd.chat.objects.ChatUser;
import com.alttd.chat.objects.ModifiableString;
import com.alttd.chat.objects.Toggleable;
import com.alttd.chat.objects.*;
import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.GalaxyUtility;
import com.alttd.chat.util.Utility;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent;
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
import io.papermc.paper.event.player.AsyncChatEvent;
@@ -87,7 +86,18 @@ public class ChatListener implements Listener {
ModifiableString modifiableString = new ModifiableString(input);
// todo a better way for this
if(!RegexManager.filterText(player.getName(), player.getUniqueId(), modifiableString, "chat")) {
if(!RegexManager.filterText(player.getName(), player.getUniqueId(), modifiableString, true, "chat", filterType -> {
if (!filterType.equals(FilterType.PUNISH)) {
ALogger.warn("Received another FilterType than punish when filtering chat and executing a filter action");
return;
}
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("punish");
out.writeUTF(player.getName());
out.writeUTF(player.getUniqueId().toString());
out.writeUTF(modifiableString.string());
player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray());
})) {
event.setCancelled(true);
GalaxyUtility.sendBlockedNotification("Language", player,
modifiableString.component(),
@@ -11,15 +11,25 @@ import com.alttd.chat.objects.Toggleable;
import com.alttd.chat.util.GalaxyUtility;
import com.alttd.chat.util.Utility;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Stack;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -86,4 +96,29 @@ public class PlayerListener implements Listener {
}
}
private final HashMap<UUID, Stack<Instant>> sendPlayerDeaths = new HashMap<>();
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerDeath(@NotNull PlayerDeathEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
Stack<Instant> playerDeathsStack = sendPlayerDeaths.computeIfAbsent(uuid, key -> new Stack<>());
Instant cutOff = Instant.now().minus(Config.DEATH_MESSAGES_LIMIT_PERIOD_MINUTES, ChronoUnit.MINUTES);
while (playerDeathsStack.peek().isBefore(cutOff)) {
playerDeathsStack.pop();
}
if (playerDeathsStack.size() > Config.DEATH_MESSAGES_MAX_PER_PERIOD || serverConfig.MUTED) {
event.deathMessage(Component.empty());
return;
} else {
Component component = event.deathMessage();
if (component != null) {
component = Component.text("* ").append(component);
component = component.style(Style.style(TextColor.color(82, 80, 77), TextDecoration.ITALIC));
event.deathMessage(component);
}
}
playerDeathsStack.push(Instant.now());
}
}
@@ -4,11 +4,14 @@ import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.objects.ChatUser;
import com.alttd.chat.objects.channels.CustomChannel;
import com.alttd.chat.util.ALogger;
import com.alttd.proxydiscordlink.DiscordLink;
import com.alttd.proxydiscordlink.lib.net.dv8tion.jda.api.EmbedBuilder;
import com.alttd.velocitychat.VelocityChat;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
@@ -16,8 +19,10 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.Optional;
import java.util.UUID;
public class PluginMessageListener {
@@ -93,6 +98,20 @@ public class PluginMessageListener {
proxy.getAllServers().forEach(registeredServer ->
registeredServer.sendPluginMessage(VelocityChat.getPlugin().getChannelIdentifier(), event.getData()));
}
case "punish" -> {
String playerName = in.readUTF();
ProxyServer proxy = VelocityChat.getPlugin().getProxy();
ConsoleCommandSource consoleCommandSource = proxy.getConsoleCommandSource();
proxy.getCommandManager().executeAsync(consoleCommandSource, String.format("ban %s Automatic ban, please appeal if you feel review is needed.", playerName));
ALogger.info(String.format("Auto banned %s due to violating the `punish` filter.", playerName));
EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setTitle("Automatic ban through the chat filter");
embedBuilder.setAuthor(playerName, null, "https://crafatar.com/avatars/" + in.readUTF() + "?overlay");
embedBuilder.setDescription(String.format("`%s`\n\n Auto permanent ban\n\n Auto banned for violating the `punish` chat filter. This could be a false positive! Their message was\n||%s||", playerName, in.readUTF()));
embedBuilder.setColor(Color.RED);
DiscordLink.getPlugin().getBot().sendEmbedToDiscord(514922317923614728L, embedBuilder, -1);
}
default -> {
VelocityChat.getPlugin().getLogger().info("server " + event.getSource());
ProxyServer proxy = VelocityChat.getPlugin().getProxy();