diff --git a/build.gradle b/build.gradle index 631f623..16e1d2a 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,8 @@ repositories { } dependencies { + implementation 'org.apache.commons:commons-text:1.10.0' // Use the version that best suits your project + // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" diff --git a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java b/src/main/java/land/chipmunk/chipmunkmod/Configuration.java index 1c12922..838e31c 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java +++ b/src/main/java/land/chipmunk/chipmunkmod/Configuration.java @@ -43,8 +43,9 @@ public class Configuration { } public static class AntiSpam { - public int matchingMessagesToBeSpam = 50; + public int matchingMessagesToBeSpam = 20; public int messageTimeInTicks = 100; - public int blockedPatternDurationInTicks = 72000; + public int blockedPatternDurationInTicks = 0; //TODO: remove + public int minimumLevenshteinDistanceToBeSpam = 20; } } diff --git a/src/main/java/land/chipmunk/chipmunkmod/mixin/ChatHudMixin.java b/src/main/java/land/chipmunk/chipmunkmod/mixin/ChatHudMixin.java index ab0b04f..24b2877 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/mixin/ChatHudMixin.java +++ b/src/main/java/land/chipmunk/chipmunkmod/mixin/ChatHudMixin.java @@ -51,10 +51,11 @@ public class ChatHudMixin { listener.chatMessageReceived(message); } - for (AntiChatSpamModule.BlockedPattern pattern : AntiChatSpamModule.instance.patterns) { - if(pattern.pattern().matcher(message.getString()).matches()) ci.cancel(); - } - new AntiChatSpamModule.ChatMessage(message.getString()); +// for (AntiChatSpamModule.BlockedPattern pattern : AntiChatSpamModule.instance.patterns) { +// if(pattern.pattern().matcher(message.getString()).matches()) ci.cancel(); +// } + AntiChatSpamModule.ChatMessage cmessage = new AntiChatSpamModule.ChatMessage(message.getString()); + if(cmessage.hidden) ci.cancel(); } } diff --git a/src/main/java/land/chipmunk/chipmunkmod/testclient/modules/utility/AntiChatSpamModule.java b/src/main/java/land/chipmunk/chipmunkmod/testclient/modules/utility/AntiChatSpamModule.java index c9bbdb2..8028c87 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/testclient/modules/utility/AntiChatSpamModule.java +++ b/src/main/java/land/chipmunk/chipmunkmod/testclient/modules/utility/AntiChatSpamModule.java @@ -1,320 +1,76 @@ package land.chipmunk.chipmunkmod.testclient.modules.utility; import land.chipmunk.chipmunkmod.ChipmunkMod; -import land.chipmunk.chipmunkmod.Configuration; import land.chipmunk.chipmunkmod.testclient.gui.components.Module; -import land.chipmunk.chipmunkmod.util.Chat; import land.chipmunk.chipmunkmod.util.Debug; -import land.chipmunk.chipmunkmod.util.Executor; import lombok.Getter; -import lombok.Setter; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.minecraft.client.MinecraftClient; +import org.apache.commons.text.similarity.LevenshteinDistance; import java.util.ArrayList; -import java.util.Comparator; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; public class AntiChatSpamModule extends Module { public static AntiChatSpamModule instance = new AntiChatSpamModule(); public ArrayList messages = new ArrayList<>(); - public ArrayList patterns = new ArrayList<>(); - private int threadCheckStateTimer = 0; private static final String debugCallerPrefix = "AntiChatSpam."; private static final String debugTickedCaller = debugCallerPrefix + "tick"; - private static final String debugThreadCaller = debugCallerPrefix + "thread"; - private static final String debugRegexProcessCaller = debugCallerPrefix + "regex_process"; - private static final String debugRegexOutputCaller = debugCallerPrefix + "regex_output"; - private static final String debugComparableStringCaller = debugCallerPrefix + "comparable_string"; + private static final String debugLevenshteinDistanceCaller = debugCallerPrefix + "levenshtein_distance"; + private static final String debugLevenshteinDistanceCallerSpamless = debugCallerPrefix + "levenshtein_distance_spamless"; + public AntiChatSpamModule() { super("Anti chat spam"); ClientTickEvents.END_CLIENT_TICK.register(client -> { for (int i = 0; i < messages.size(); i++) { - Debug.debug("Ticked message: "+messages.get(i).content, debugTickedCaller); + Debug.debug("Ticked message: " + messages.get(i).content, debugTickedCaller); messages.get(i).tick(); } - for (int i = 0; i < patterns.size(); i++) { - Debug.debug("Ticked pattern: "+patterns.get(i).pattern().toString(), debugTickedCaller); - patterns.get(i).tick(); - } - if(threadCheckStateTimer >= 10) { - Debug.debug("Thread done: "+thread.isDone(), debugThreadCaller); - threadCheckStateTimer = -1; - } - threadCheckStateTimer++; }); - - // ig make theredaadd thingy here because else it says it's null and stuff or idk - thread = Executor.submit(() -> { - boolean interrupted = false; - String caller = debugThreadCaller; - while(!interrupted) { - Debug.debug("woke up", caller); - while(threadQueue.size()>0) { - threadQueue.get(0).run(); - threadQueue.remove(0); - Debug.debug("ran a thing in the queue now "+threadQueue.size()+" left", caller); - } - Debug.debug("going zzz", caller); - try { - Thread.sleep(50); - } catch (InterruptedException e) { - interrupted = true; - Debug.debug("Thread interrupted by exception!", caller); - } catch (Exception e) { - Debug.debug("Got an exception or idk printing trace", caller); - e.printStackTrace(); - interrupted = true; - } - } - Debug.debug("out of while loop or idk bye ig!!!", caller); - }); - - } // 72000 ticks for expiry - // and ig remember messages for 100 ticks - - public static class ComparableString{ - @Getter @Setter private String value; - public int index = 0; - public ComparableString(String value) { - Debug.debug("Created comparable string with value: "+value, debugComparableStringCaller); - this.value = value; - } - public String getChar() { - if (index < 0 || value.length() < index) throw new RuntimeException("Index is out of bounds! (INDEX:"+index+", LENGTH:"+value.length()+")"); - String toReturn = switch (value.charAt(index)) { - case '.' -> "\\."; - case '[' -> "\\["; - case ']' -> "\\]"; - case '(' -> "\\("; - case ')' -> "\\)"; - case '*' -> "\\*"; - case '?' -> "\\?"; - default -> String.valueOf(value.charAt(index)); - }; - Debug.debug("Got character: "+toReturn, debugComparableStringCaller); - return toReturn; - } - - public void repeat(RepeatableRunnable runnable) { - while(index characters = new ArrayList<>(); - firstString.index = 0; - firstString.repeat(i -> { - Debug.debug("[A] Char index "+i+": "+firstString.getChar(), debugRegexProcessCaller); - secondString.index = 0; - secondString.repeat(j -> { - Debug.debug(" [B] Char index "+j+": "+secondString.getChar(), debugRegexProcessCaller); - if(firstString.getChar().equals(secondString.getChar())) { - Debug.debug("OMGOMG THEY MATCH adding to characters array", debugRegexProcessCaller); - characters.add(new CharacterInPattern(firstString.getChar(), i, j)); - return true; - } - return false; - }); - return false; - }); - - Debug.debug("--- SORTING CHARS ---", debugRegexProcessCaller); - // sort based on index sum - Comparator comparator = new Comparator() { - @Override - public int compare(CharacterInPattern character1, CharacterInPattern character2) { - return Integer.compare(character1.indexesSum(), character2.indexesSum()); - } - }; - characters.sort(comparator); - for (CharacterInPattern character : characters) { - Debug.debug(character.indexesSum() + " - " + character.character(), debugRegexProcessCaller); - } - - - Debug.debug("--- CONFIRMING & PLACING CHARACTERS ---", debugRegexProcessCaller); - ArrayList confirmedCharacters = new ArrayList<>(); - for (int k = 0, charactersSize = characters.size(); k < charactersSize; k++) { - CharacterInPattern character = characters.get(k); - Debug.debug("Finding if B has been placed", debugRegexProcessCaller); - //find if index B has been placed somewhere - AtomicBoolean isIndexBTaken = new AtomicBoolean(false); - confirmedCharacters.forEach(character1 -> { - if (character.indexB() == character1.indexB()) isIndexBTaken.set(true); - }); - - // if B has already been placed somewhere, resume analysis - if (isIndexBTaken.get()) { - Debug.debug("It has! resuping analysis", debugRegexProcessCaller); - firstString.index = character.indexA(); - secondString.index = character.indexB()+1; - Debug.debug("[A] Char index " + firstString.index + ": " + firstString.getChar(), debugRegexProcessCaller); - secondString.repeat(j -> { - Debug.debug(" [B] Char index " + j + ": " + secondString.getChar(), debugRegexProcessCaller); - if (firstString.getChar().equals(secondString.getChar())) { - Debug.debug("OMGOMG THEY MATCH adding to characters array", debugRegexProcessCaller); -// characters.add(new CharacterInPattern(firstString.getChar(), firstString.index, j)); - for (int i = 0, sortedCharactersSize = characters.size(); i < sortedCharactersSize; i++) { - CharacterInPattern sortedCharacter = characters.get(i); - if (sortedCharacter.indexesSum() < j) { - characters.add(i, new CharacterInPattern(firstString.getChar(), firstString.index, j)); - Debug.debug("Added to sorted chars at index " + i, debugRegexProcessCaller); - break; - } - } - return true; - } - return false; - }); - } - - // check if you can place it somewhere in the output - // this means that one index must never be greater than another character's if the other is not. - - // if it's the first character, add it no matter what. No need to check lol - if (character.indexesSum() == 0) { - Debug.debug("First char so putting it at start of output", debugRegexProcessCaller); - confirmedCharacters.add(character); - continue; - } - - // if output is empty and it's not the first character, add a .*? and then the character - if (confirmedCharacters.isEmpty()) { - Debug.debug("Not first char but output empty so putting .*? then char at start", debugRegexProcessCaller); - confirmedCharacters.add(new CharacterInPattern(".*?", 0, 0)); - confirmedCharacters.add(character); - continue; - } - - Debug.debug("Not at the start so checking where tf it is", debugRegexProcessCaller); - // end of special cases now just check if u can place it somewhere - AtomicInteger index = new AtomicInteger(); - boolean rightAfter = false; - for (int i = 0; i < confirmedCharacters.size(); i++) { - CharacterInPattern character1 = confirmedCharacters.get(i); - Debug.debug("Checking character " + character1.character(), debugRegexProcessCaller); - // right after the character - if (character.indexA() == character1.indexA() + 1 && character.indexB() == character1.indexB() + 1) { - Debug.debug("It's right after the char so placing it there", debugRegexProcessCaller); - i++; - if (i >= confirmedCharacters.size()) confirmedCharacters.add(character); - else confirmedCharacters.add(i, character); - rightAfter = true; - break; - } - } - for (int i = 0; i < confirmedCharacters.size() && !rightAfter; i++) { - CharacterInPattern character1 = confirmedCharacters.get(i); - Debug.debug("Checking character " + character1.character(), debugRegexProcessCaller); - //somewhere else after che character - if (character1.indexA() < character.indexA() && character1.indexB() < character.indexB()) { - Debug.debug("It's somewhere after the char so putting it there with .*? in front", debugRegexProcessCaller); - if (index.get() >= confirmedCharacters.size()) { - i++; - confirmedCharacters.add(new CharacterInPattern(".*?", character1.indexA() + 1, character.indexB() + 1)); - i++; - confirmedCharacters.add(character); - } else { - i++; - confirmedCharacters.add(i, new CharacterInPattern(".*?", character1.indexA() + 1, character.indexB() + 1)); - i++; - confirmedCharacters.add(i, character); - } - } - - index.getAndIncrement(); - } - - } - StringBuilder regex = new StringBuilder(); - for (int i = 0, confirmedCharactersSize = confirmedCharacters.size(); i < confirmedCharactersSize; i++) { - CharacterInPattern c = confirmedCharacters.get(i); - regex.append(c.character); - } - Debug.debug("Returning regex "+regex, debugRegexProcessCaller); - Debug.debug("Returning regex '"+regex+"' for strings:", debugRegexOutputCaller); - Debug.debug(" - "+firstString, debugRegexOutputCaller); - Debug.debug(" - "+secondString, debugRegexOutputCaller); - return Pattern.compile(regex.toString()); - } - - public static class CharacterInPattern { - @Getter private final String character; - @Getter private final int indexA; - @Getter private final int indexB; // setter so I can change it while resuming analysis later on - @Getter private final int indexesSum; - public CharacterInPattern(String character, int indexA, int indexB) { - this.character = character; - this.indexA = indexA; - this.indexB = indexB; - indexesSum = indexA + indexB; - } - } - - public interface RepeatableRunnable{ - /** - * @param i the current index - * @return if the loop should break - */ - boolean run(int i); // to repeat for the length of the string ig - } public static class ChatMessage { - @Getter private final String content; + @Getter + private final String content; + public boolean hidden = false; public int timer = ChipmunkMod.CONFIG.antiSpam.messageTimeInTicks; + public ChatMessage(String content) { this.content = content; - threadQueue.add(() -> { - ArrayList chatMessages = instance.messages; - for (int i = 0; i < chatMessages.size(); i++) { - ChatMessage message = chatMessages.get(i); - Pattern pattern = getPattern(new ComparableString(this.content()), new ComparableString(message.content())); - int matching = 0; - for (ChatMessage message1 : instance.messages) { - if (pattern.matcher(message1.content()).matches()) matching++; - } - if (matching >= ChipmunkMod.CONFIG.antiSpam.matchingMessagesToBeSpam) { - instance.patterns.add(new BlockedPattern(pattern)); - } - } - }); - + ArrayList chatMessages = instance.messages; + int similarMessages = 0; + LevenshteinDistance ld = new LevenshteinDistance(); // thanks maniaplay fo r teaching me about levenshtein distance + for (int i = 0; i < chatMessages.size(); i++) { + ChatMessage message = chatMessages.get(i); + int distance = ld.apply(this.content(), message.content()); + Debug.debug("Distance: " + distance, debugLevenshteinDistanceCaller); + if (distance <= ChipmunkMod.CONFIG.antiSpam.minimumLevenshteinDistanceToBeSpam) similarMessages++; +// Pattern pattern = getPattern(new ComparableString(this.content()), new ComparableString(message.content())); +// int matching = 0; +// ArrayList chatMessageArrayList = instance.messages; +// for (int j = 0; j < chatMessageArrayList.size(); j++) { +// ChatMessage message1 = chatMessageArrayList.get(j); +// if (pattern.matcher(message1.content()).matches()) matching++; +// } +// if (matching >= ChipmunkMod.CONFIG.antiSpam.matchingMessagesToBeSpam) { +// instance.patterns.add(new BlockedPattern(pattern)); +// } + } + Debug.debug("Similar messages: " + similarMessages, debugLevenshteinDistanceCaller); + Debug.debug("Similar messages: " + similarMessages, debugLevenshteinDistanceCallerSpamless); + if (similarMessages >= ChipmunkMod.CONFIG.antiSpam.matchingMessagesToBeSpam) hidden = true; + Debug.debug("Hidden: " + hidden, debugLevenshteinDistanceCaller); + Debug.debug("Hidden: " + hidden, debugLevenshteinDistanceCallerSpamless); instance.messages.add(this); - } - public void tick() {timer--; if(timer <= 0) instance.messages.remove(this);} - } +// threadQueue.add(() -> { +// // code above used to be here but i cant decide if i should show it or not depending on the thread cuz i cant make it wait +// }); - public static class BlockedPattern { - @Getter private final Pattern pattern; - public int timer = ChipmunkMod.CONFIG.antiSpam.blockedPatternDurationInTicks; - public BlockedPattern(Pattern pattern) { - this.pattern = pattern; - instance.patterns.add(this); - Chat.send("[AntiSpam] Blocked pattern "+pattern.toString()); } - public void tick() {timer--; if(timer <= 0) instance.patterns.remove(this);} - } - public static ArrayList threadQueue = new ArrayList<>(); - public static Future thread; - public static void debug(String message) { - Debug.debug(message, "AntiChatSpam"); + public void tick() { + timer--; + if (timer <= 0) instance.messages.remove(this); + } } -} // holy shit 200 lines and i havent ran this once this is not going to go well -// it did not go well \ No newline at end of file +} \ No newline at end of file