diff options
author | Kendell R <KTibow@users.noreply.github.com> | 2022-05-01 07:47:57 -0700 |
---|---|---|
committer | Kendell R <KTibow@users.noreply.github.com> | 2022-05-01 07:48:17 -0700 |
commit | 2eded39259f3b717cce1a58e7fdabf6852ffdfcb (patch) | |
tree | 3acdef2a8f67a54ed9a1ab6e1414bb0953268438 /src/main/kotlin | |
parent | d3b3d5273d6754c991d6ba9a69884071a8e56c91 (diff) | |
download | Chatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.tar.gz Chatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.tar.bz2 Chatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.zip |
feat: spam block
Diffstat (limited to 'src/main/kotlin')
3 files changed, 152 insertions, 0 deletions
diff --git a/src/main/kotlin/cc/woverflow/chatting/Chatting.kt b/src/main/kotlin/cc/woverflow/chatting/Chatting.kt index 0fe85f9..5718f14 100644 --- a/src/main/kotlin/cc/woverflow/chatting/Chatting.kt +++ b/src/main/kotlin/cc/woverflow/chatting/Chatting.kt @@ -2,6 +2,7 @@ package cc.woverflow.chatting import cc.woverflow.chatting.chat.ChatSearchingManager import cc.woverflow.chatting.chat.ChatShortcuts +import cc.woverflow.chatting.chat.ChatSpamBlock import cc.woverflow.chatting.chat.ChatTabs import cc.woverflow.chatting.config.ChattingConfig import cc.woverflow.chatting.hook.GuiNewChatHook @@ -80,6 +81,7 @@ object Chatting { } ClientRegistry.registerKeyBinding(keybind) EVENT_BUS.register(this) + EVENT_BUS.register(ChatSpamBlock) ChatTabs.initialize() ChatShortcuts.initialize() } diff --git a/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt new file mode 100644 index 0000000..224e614 --- /dev/null +++ b/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt @@ -0,0 +1,124 @@ +package cc.woverflow.chatting.chat + +import cc.woverflow.chatting.config.ChattingConfig +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.text.Normalizer + +object ChatSpamBlock { + /* + Made by @KTibow + Based off of Unspam (also by @KTibow) + Algorithm based off of https://paulgraham.com/spam.html + */ + private val PLAYER_MESSAGE = Regex("^(\\[VIP\\+?\\] |\\[MVP\\+?\\+?\\] |)(\\w{2,16}): (.*)$") + + @SubscribeEvent + fun onChat(event: ClientChatReceivedEvent) { + val message = event.message.unformattedText.replace(Regex("\u00A7."), "") + if (!PLAYER_MESSAGE.matches(message)) return + val (rank, player, content) = PLAYER_MESSAGE.matchEntire(message)!!.destructured + val tokens = tokenize(content) + val spamProb = findSpamProbability(tokens) + println("\n[CHATTING]$message") + if (spamProb * 100 > ChattingConfig.spamThreshold) { + if (ChattingConfig.showSpamInGray) { + var newMessage = EnumChatFormatting.DARK_GRAY.toString() + EnumChatFormatting.STRIKETHROUGH.toString() + if (!ChattingConfig.customFormatting) { + newMessage += rank + } + newMessage += "$player${EnumChatFormatting.DARK_GRAY.toString()}: $content" + event.message = ChatComponentText(newMessage) + } else { + event.isCanceled = true + } + return + } + if (ChattingConfig.customFormatting) { + val coloredPlayer = findRankColor(rank) + player + EnumChatFormatting.RESET.toString() + event.message = ChatComponentText("$coloredPlayer: $content") + } + } + + private fun tokenize(message: String): MutableList<String> { + val strippedMessage = + Normalizer.normalize(message, Normalizer.Form.NFKC).replace(Regex("[^\\w\\s/]"), " ").lowercase().trim() + val tokens = strippedMessage.split(Regex("\\s+")).toMutableList() + if (tokens.size <= 2) { + tokens.add("TINY_LENGTH") + } else if (tokens.size <= 4) { + tokens.add("SMALL_LENGTH") + } else if (tokens.size <= 7) { + tokens.add("MEDIUM_LENGTH") + } else { + tokens.add("LONG_LENGTH") + } + if (message.replace(Regex("[\\w\\s]"), "").length > 2) { + tokens.add("SPECIAL_CHARS") + } else if (message.replace(Regex("[\\w\\s]"), "").length > 0) { + tokens.add("SPECIAL_CHAR") + } else { + tokens.add("LOW_SPECIAL_CHARS") + } + if (message.replace(Regex("[^A-Z]"), "").length >= message.length / 4) { + tokens.add("HIGH_CAPS") + } else { + tokens.add("LOW_CAPS") + } + return tokens + } + + private fun findSpamProbability(tokens: MutableList<String>): Double { + val tokenProbs = mutableMapOf<String, Double>() + for (token in tokens) { + if (!spamInfoJson.has(token)) continue + val spamInToken = spamInfoJson.get(token).asJsonObject.get("spam").asDouble + val fineInToken = spamInfoJson.get(token).asJsonObject.get("fine").asDouble + tokenProbs[token] = ( + (spamInToken / messageCountsJson.get("spam").asInt) / + (fineInToken / messageCountsJson.get("fine").asInt + + spamInToken / messageCountsJson.get("spam").asInt) + ) + } + val spamProbs = tokenProbs.values.toMutableList() + val fineProbs = tokenProbs.values.map { + 1 - it + }.toMutableList() + val spamProbability = spamProbs.reduce { a, b -> + a * b + } + val fineProbability = fineProbs.reduce { a, b -> + a * b + } + return spamProbability / (spamProbability + fineProbability) + } + + private fun findRankColor(rank: String): String { + println(rank) + return when (rank) { + "[VIP] ", "[VIP+] " -> EnumChatFormatting.GREEN.toString() + "[MVP] ", "[MVP+] " -> EnumChatFormatting.AQUA.toString() + "[MVP++] " -> EnumChatFormatting.GOLD.toString() + else -> EnumChatFormatting.GRAY.toString() + } + } + + private fun getResourceAsText(path: String): String? = object {}.javaClass.getResource(path)?.readText() + private val spamInfoJson: JsonObject + private val messageCountsJson: JsonObject + + init { + // Load the file spamInfo.json from resources/ + val spamInfo = getResourceAsText("/spamInfo.json") + spamInfoJson = JsonParser().parse(spamInfo).asJsonObject + messageCountsJson = JsonParser().parse(" { \"fine\": 668, \"spam\": 230 }").asJsonObject + } +} + + + + diff --git a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt b/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt index 93a8723..24729be 100644 --- a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt +++ b/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt @@ -79,6 +79,32 @@ object ChattingConfig : var informForAlternatives = true @Property( + type = PropertyType.SLIDER, + min = 0, + max = 100, + name = "Spam Threshold", + description = "If Chatting detects a player message seems like spam, and the probability is above this threshold, it will hide it. Set to 0 to disable.", + category = "Player Spam Blocker" + ) + var spamThreshold = 95 + + @Property( + type = PropertyType.SWITCH, + name = "Show Spam (with styling)", + description = "Show messages Chatting detects as spam in gray, instead of hiding them.", + category = "Player Spam Blocker" + ) + var showSpamInGray = true + + @Property( + type = PropertyType.SWITCH, + name = "Custom Message Formatting", + description = "Hide ranks, and show messages in public chat from no-ranks as white.", + category = "Player Spam Blocker" + ) + var customFormatting = false + + @Property( type = PropertyType.SWITCH, name = "Custom Chat Height", description = "Allows you to change the height of chat to heights greater than before.", |