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-01 07:47:57 -0700
committerKendell R <KTibow@users.noreply.github.com>2022-05-01 07:48:17 -0700
commit2eded39259f3b717cce1a58e7fdabf6852ffdfcb (patch)
tree3acdef2a8f67a54ed9a1ab6e1414bb0953268438 /src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt
parentd3b3d5273d6754c991d6ba9a69884071a8e56c91 (diff)
downloadChatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.tar.gz
Chatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.tar.bz2
Chatting-2eded39259f3b717cce1a58e7fdabf6852ffdfcb.zip
feat: spam block
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..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
+ }
+}
+
+
+
+