From f9f22a5f61d129a17073a795537dcb222907964f Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 17 Sep 2022 19:30:57 +0200 Subject: added player chat filter --- .../java/at/hannibal2/skyhanni/SkyHanniMod.java | 2 + .../hannibal2/skyhanni/config/features/Chat.java | 40 ++++++---- .../skyhanni/events/PlayerSendChatEvent.kt | 3 +- .../features/chat/playerchat/PlayerChatFilter.kt | 89 ++++++++++++++++++++++ .../chat/playerchat/PlayerChatFormatter.kt | 52 ++++++++----- .../features/fishing/SeaCreatureManager.kt | 2 +- .../at/hannibal2/skyhanni/utils/MultiFilter.kt | 40 +++++++--- 7 files changed, 184 insertions(+), 44 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFilter.kt (limited to 'src/main/java/at/hannibal2/skyhanni') diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index 1dd8ad6ef..0180f3fb2 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -12,6 +12,7 @@ import at.hannibal2.skyhanni.features.bazaar.BazaarBestSellMethod; import at.hannibal2.skyhanni.features.bazaar.BazaarOrderHelper; import at.hannibal2.skyhanni.features.chat.ChatFilter; import at.hannibal2.skyhanni.features.chat.PlayerDeathMessages; +import at.hannibal2.skyhanni.features.chat.playerchat.PlayerChatFilter; import at.hannibal2.skyhanni.features.chat.playerchat.PlayerChatFormatter; import at.hannibal2.skyhanni.features.commands.WikiCommand; import at.hannibal2.skyhanni.features.damageindicator.DamageIndicatorManager; @@ -130,6 +131,7 @@ public class SkyHanniMod { registerEvent(new HideMobNames()); registerEvent(new HideDamageSplash()); registerEvent(new ThunderSparksHighlight()); + registerEvent(new PlayerChatFilter()); Commands.init(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Chat.java b/src/main/java/at/hannibal2/skyhanni/config/features/Chat.java index e8e424b0d..d00813c2b 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Chat.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Chat.java @@ -46,22 +46,28 @@ public class Chat { @ConfigEditorAccordion(id = 1) public boolean playerMessages = false; + @Expose + @ConfigOption(name = "Message Format", desc = "") + @ConfigAccordionId(id = 1) + @ConfigEditorAccordion(id = 2) + public boolean messageFormat = false; + @Expose @ConfigOption(name = "All Channel Prefix", desc = "Show the prefix for the all channel chat.") @ConfigEditorBoolean - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public boolean allChannelPrefix = false; @Expose @ConfigOption(name = "Player Rank Hider", desc = "Hide player ranks in the chat.") @ConfigEditorBoolean - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public boolean playerRankHider = false; @Expose @ConfigOption(name = "Player Colon Hider", desc = "Hide the colon after the player name in the chat.") @ConfigEditorBoolean - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public boolean playerColonHider = false; @Expose @@ -76,7 +82,7 @@ public class Chat { "§bname §8[§6123§8]§f: msg", "§cHide SkyBlock Level"} ) - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public int skyblockLevelDesign = 0; @Expose @@ -89,7 +95,7 @@ public class Chat { "§6§l⌬499", "§cHide Elite Position"} ) - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public int eliteFormat = 0; @Expose @@ -104,27 +110,33 @@ public class Chat { "§8[§2G§8]", "§8(§2G§8)"} ) - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public int channelDesign = 0; - @Expose - @ConfigOption(name = "NEU Profile Viewer", desc = "Click on a player name to open the Profile Viewer from NotEnoughUpdates") - @ConfigEditorBoolean - @ConfigAccordionId(id = 1) - public boolean neuProfileViewer = false; - @Expose @ConfigOption(name = "Test All Chat", desc = "Test the All Chat message format locally (no message gets sent to hypixel)") @ConfigEditorButton(runnableId = "testAllChat") - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public boolean testAllChat = false; @Expose @ConfigOption(name = "Test Guild Chat", desc = "Test the Guild Chat message format locally (no message gets sent to hypixel)") @ConfigEditorButton(runnableId = "testGuildChat") - @ConfigAccordionId(id = 1) + @ConfigAccordionId(id = 2) public boolean testGuildChat = false; + @Expose + @ConfigOption(name = "NEU Profile Viewer", desc = "Click on a player name to open the Profile Viewer from NotEnoughUpdates") + @ConfigEditorBoolean + @ConfigAccordionId(id = 1) + public boolean neuProfileViewer = false; + + @Expose + @ConfigOption(name = "Chat Filter", desc = "Scan messages sent by players in all-chat for blacklisted words and greys out the message") + @ConfigEditorBoolean + @ConfigAccordionId(id = 1) + public boolean chatFilter = false; + @Expose @ConfigOption(name = "Dungeon Filter", desc = "Hide annoying messages in the dungeon.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/events/PlayerSendChatEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/PlayerSendChatEvent.kt index 4ed17e749..12b9300e4 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/PlayerSendChatEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/PlayerSendChatEvent.kt @@ -6,6 +6,7 @@ import net.minecraft.util.ChatComponentText class PlayerSendChatEvent( val channel: PlayerMessageChannel, val name: String, - var message: String, + val message: String, + val chatComponents: MutableList, var cancelledReason: String = "", ) : LorenzEvent() \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFilter.kt new file mode 100644 index 000000000..fe0cc52b7 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFilter.kt @@ -0,0 +1,89 @@ +package at.hannibal2.skyhanni.features.chat.playerchat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.PlayerSendChatEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.MultiFilter +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class PlayerChatFilter { + + private val filters = mutableMapOf() + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + filters.clear() + var countCategories = 0 + var countFilters = 0 + + try { + val data = event.getConstant("PlayerChatFilter")!! + + for (category in data["filters"].asJsonArray) { + val jsonObject = category.asJsonObject + val description = jsonObject["description"].asString + val filter = MultiFilter() + filter.load(jsonObject) + filters[description] = filter + + countCategories++ + countFilters += filter.count() + } + + LorenzUtils.debug("Loaded $countFilters filters in $countCategories categories from repo") + + } catch (e: Exception) { + e.printStackTrace() + LorenzUtils.error("error in RepositoryReloadEvent") + } + } + + @SubscribeEvent(priority = EventPriority.HIGH, receiveCanceled = true) + fun onEvent(event: PlayerSendChatEvent) { + if (!SkyHanniMod.feature.chat.chatFilter) return + if (event.channel != PlayerMessageChannel.ALL) return + + val message = event.message.lowercase() + for (filter in filters) { + filter.value.matchResult(message)?.let { + filter(event, it) + return + } + } + } + + private fun filter(event: PlayerSendChatEvent, filter: String) { + val pattern = Pattern.compile("(.*)?$filter(.*)?", Pattern.CASE_INSENSITIVE) + val matcher = pattern.matcher(event.message) + matcher.matches() + val beginning = matcher.group(1) + val end = matcher.group(2) + + event.chatComponents.clear() + + val endSplit = end.split(" ") + + for (word in beginning.split(" ")) { + if (word.isEmpty()) continue + event.chatComponents.add(coloredChat(word, EnumChatFormatting.GRAY)) + } + + event.chatComponents.add(coloredChat(filter.trim(), EnumChatFormatting.WHITE)) + + for (word in endSplit) { + if (word.isEmpty()) continue + event.chatComponents.add(coloredChat(word, EnumChatFormatting.GRAY)) + } + } + + private fun coloredChat(string: String, color: EnumChatFormatting): ChatComponentText { + val text = ChatComponentText(string) + text.chatStyle.color = color + return text + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFormatter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFormatter.kt index 39b8458f2..16b4698cb 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFormatter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/playerchat/PlayerChatFormatter.kt @@ -31,6 +31,9 @@ class PlayerChatFormatter { //"§6Ⓑ" // bingo rank 4 ) + private val patternUrl = + Pattern.compile("^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$") + //§6[⌬57] §r§8[§r§b235§r§8] §r§6[MVP§r§c++§r§6] hannibal2§r§f: Hello World! private val patternElitePrefix = Pattern.compile("§6\\[⌬(\\d+)] (.+)") @@ -41,9 +44,6 @@ class PlayerChatFormatter { //§dTo §r§b[MVP§r§3+§r§b] Skyfall55§r§7: §r§7hello :) private var patternPrivateMessage: Pattern = Pattern.compile("(§d)+(To|From) §r(.+)§r§7: §r§7(.+)") - private val patternUrl = - Pattern.compile("^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$") - @SubscribeEvent fun onChatMessage(event: LorenzChatEvent) { if (!LorenzUtils.isOnHypixel) return @@ -53,6 +53,18 @@ class PlayerChatFormatter { } } + @SubscribeEvent(receiveCanceled = true) + fun onChatWithUrl(event: PlayerSendChatEvent) { + if (!LorenzUtils.inSkyblock) return + + for (chatComponent in event.chatComponents) { + val text = chatComponent.unformattedText + if (patternUrl.matcher(text).matches()) { + chatComponent.chatStyle.chatClickEvent = ClickEvent(ClickEvent.Action.OPEN_URL, text) + } + } + } + private fun shouldBlock(originalMessage: String): Boolean { if (handlePrivateMessage(originalMessage)) return true @@ -208,7 +220,14 @@ class PlayerChatFormatter { ) { val cleanName = grabName(formattedName, true)!! loggerPlayerChat.log("[$channel] $cleanName: $message") - val event = PlayerSendChatEvent(channel, cleanName, message) + + + val chatComponents = mutableListOf() + for (line in message.split(" ")) { + chatComponents.add(ChatComponentText(line)) + } + + val event = PlayerSendChatEvent(channel, cleanName, message, chatComponents) event.postAndCatch() if (event.cancelledReason != "") { @@ -229,24 +248,21 @@ class PlayerChatFormatter { ) } - addChat(nameText, event.message) + addChat(nameText, chatComponents) } - private fun addChat(text: ChatComponentText, message: String) { - val fullText = ChatComponentText("") - fullText.appendSibling(text) - for (word in message.split(" ")) { - fullText.appendSibling(ChatComponentText(" ")) - if (patternUrl.matcher(word).matches()) { - val oneWord = ChatComponentText(word) - oneWord.chatStyle.chatClickEvent = ClickEvent(ClickEvent.Action.OPEN_URL, word) - fullText.appendSibling(oneWord) - } else { - fullText.appendSibling(ChatComponentText(word)) - } + private fun addChat(prefixComponent: ChatComponentText, chatComponents: MutableList) { + val empty = ChatComponentText(" ") + + val result = ChatComponentText("") + result.appendSibling(prefixComponent) + + for (chatComponent in chatComponents) { + result.appendSibling(empty) + result.appendSibling(chatComponent) } - Minecraft.getMinecraft().thePlayer.addChatMessage(fullText) + Minecraft.getMinecraft().thePlayer.addChatMessage(result) } private fun getLevelFormat(name: String, level: Int, levelColor: String): String { diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureManager.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureManager.kt index 8e558e23f..16fee2d35 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/SeaCreatureManager.kt @@ -29,7 +29,7 @@ class SeaCreatureManager { counter++ } } - LorenzUtils.debug("loaded $counter sea creatures from repo") + LorenzUtils.debug("Loaded $counter sea creatures from repo") } catch (e: Exception) { e.printStackTrace() diff --git a/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt b/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt index 5786cb9e7..947019aa6 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt @@ -4,21 +4,24 @@ import com.google.gson.JsonObject class MultiFilter { - val equals = mutableListOf() - val startsWith = mutableListOf() - val endsWith = mutableListOf() - val contains = mutableListOf() + private val equals = mutableListOf() + private val startsWith = mutableListOf() + private val endsWith = mutableListOf() + private val contains = mutableListOf() + private val containsWord = mutableListOf() fun load(hideNpcSell: JsonObject) { equals.clear() startsWith.clear() endsWith.clear() contains.clear() + containsWord.clear() fill(hideNpcSell, "equals", equals) fill(hideNpcSell, "startsWith", startsWith) fill(hideNpcSell, "endsWith", endsWith) fill(hideNpcSell, "contains", contains) + fill(hideNpcSell, "containsWord", containsWord) } private fun fill(jsonObject: JsonObject, key: String, list: MutableList) { @@ -27,12 +30,29 @@ class MultiFilter { } } - fun match(name: String): Boolean { - if (equals.contains(name)) return true - if (startsWith.any { name.startsWith(it) }) return true - if (endsWith.any { name.endsWith(it) }) return true - if (contains.any { name.contains(it) }) return true + fun match(string: String): Boolean { + return matchResult(string) != null + } + + fun matchResult(string: String): String? { + var result = equals.find { it == string } + if (result != null) return result + result = startsWith.find { string.startsWith(it) } + if (result != null) return result + result = endsWith.find { string.endsWith(it) } + if (result != null) return result + result = contains.find { string.contains(it) } + if (result != null) return result + result = containsWord.find { containsWord(string, it) } + if (result != null) return result + + return null + } + + private fun containsWord(message: String, word: String): Boolean = + message.startsWith("$word ") || message.endsWith(" $word") || message.contains(" $word ") - return false + fun count(): Int { + return equals.size + startsWith.size + endsWith.size + contains.size + containsWord.size } } \ No newline at end of file -- cgit