aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt
diff options
context:
space:
mode:
authorKendell R <KTibow@users.noreply.github.com>2022-05-29 12:39:36 -0700
committerKendell R <KTibow@users.noreply.github.com>2022-05-29 12:41:30 -0700
commit93a32bbbd183cf45af10a7fbe9a5238585eb442e (patch)
tree6ea38aeaca53beb1584007ae8268dcbe905269ee /src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt
parent5da5f6efd284076b65d1811f13e1b4825c34d2db (diff)
downloadChatting-93a32bbbd183cf45af10a7fbe9a5238585eb442e.tar.gz
Chatting-93a32bbbd183cf45af10a7fbe9a5238585eb442e.tar.bz2
Chatting-93a32bbbd183cf45af10a7fbe9a5238585eb442e.zip
spam block v2
Diffstat (limited to 'src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt')
-rw-r--r--src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt124
1 files changed, 124 insertions, 0 deletions
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..91ea6b5
--- /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 java.text.Normalizer
+import net.minecraft.util.ChatComponentText
+import net.minecraft.util.EnumChatFormatting
+import net.minecraftforge.client.event.ClientChatReceivedEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+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
+
+ if (ChattingConfig.spamThreshold != 100) {
+ val tokens = tokenize(content)
+ val spamProb = findSpamProbability(tokens)
+ if (spamProb * 100 > ChattingConfig.spamThreshold) {
+ if (ChattingConfig.hideSpam) {
+ event.isCanceled = true
+ } else {
+ var newMessage =
+ EnumChatFormatting.DARK_GRAY.toString() +
+ EnumChatFormatting.STRIKETHROUGH.toString()
+ if (!ChattingConfig.customChatFormatting) {
+ newMessage += rank
+ }
+ newMessage += "$player${EnumChatFormatting.DARK_GRAY.toString()}: $content"
+ event.message = ChatComponentText(newMessage)
+ }
+ return
+ }
+ }
+ if (ChattingConfig.customChatFormatting) {
+ 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
+ }
+}