Improved spam detection.
Now blocking poetry spam and padded message spam.
This commit is contained in:
parent
ec87939497
commit
bed2e11c59
|
|
@ -131,11 +131,11 @@ public class BlockEventHandler implements Listener
|
|||
|
||||
//if not empty and wasn't the same as the last sign, log it and remember it for later
|
||||
PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId());
|
||||
if(notEmpty && playerData.lastMessage != null && !playerData.lastMessage.equals(signMessage))
|
||||
if(notEmpty && playerData.lastSignMessage != null && !playerData.lastSignMessage.equals(signMessage))
|
||||
{
|
||||
GriefPrevention.AddLogEntry(player.getName() + lines.toString().replace("\n ", ";"), null);
|
||||
PlayerEventHandler.makeSocialLogEntry(player.getName(), signMessage);
|
||||
playerData.lastMessage = signMessage;
|
||||
playerData.lastSignMessage = signMessage;
|
||||
|
||||
if(!player.hasPermission("griefprevention.eavesdropsigns"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,10 +86,6 @@ public class PlayerData
|
|||
|
||||
//spam
|
||||
private Date lastLogin = null; //when the player last logged into the server
|
||||
public String lastMessage = ""; //the player's last chat message, or slash command complete with parameters
|
||||
public Date lastMessageTimestamp = new Date(); //last time the player sent a chat message or used a monitored slash command
|
||||
public int spamCount = 0; //number of consecutive "spams"
|
||||
public boolean spamWarned = false; //whether the player recently received a warning
|
||||
|
||||
//visualization
|
||||
public Visualization currentVisualization = null;
|
||||
|
|
@ -135,6 +131,9 @@ public class PlayerData
|
|||
//this is an anti-bot strategy.
|
||||
Location noChatLocation = null;
|
||||
|
||||
//last sign message, to prevent sign spam
|
||||
String lastSignMessage = null;
|
||||
|
||||
//ignore list
|
||||
//true means invisible (admin-forced ignore), false means player-created ignore
|
||||
public ConcurrentHashMap<UUID, Boolean> ignoredPlayers = new ConcurrentHashMap<UUID, Boolean>();
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ class PlayerEventHandler implements Listener
|
|||
//matcher for banned words
|
||||
private WordFinder bannedWordFinder = new WordFinder(GriefPrevention.instance.dataStore.loadBannedWords());
|
||||
|
||||
//spam tracker
|
||||
SpamDetector spamDetector = new SpamDetector();
|
||||
|
||||
//typical constructor, yawn
|
||||
PlayerEventHandler(DataStore dataStore, GriefPrevention plugin)
|
||||
{
|
||||
|
|
@ -207,14 +210,7 @@ class PlayerEventHandler implements Listener
|
|||
}
|
||||
}
|
||||
|
||||
//last chat message shown, regardless of who sent it
|
||||
private String lastChatMessage = "";
|
||||
private long lastChatMessageTimestamp = 0;
|
||||
|
||||
//number of identical messages in a row
|
||||
private int duplicateMessageCount = 0;
|
||||
|
||||
//returns true if the message should be sent, false if it should be muted
|
||||
//returns true if the message should be muted, true if it should be sent
|
||||
private boolean handlePlayerChat(Player player, String message, PlayerEvent event)
|
||||
{
|
||||
//FEATURE: automatically educate players about claiming land
|
||||
|
|
@ -250,10 +246,16 @@ class PlayerEventHandler implements Listener
|
|||
//if the player has permission to spam, don't bother even examining the message
|
||||
if(player.hasPermission("griefprevention.spam")) return false;
|
||||
|
||||
boolean spam = false;
|
||||
String mutedReason = null;
|
||||
//examine recent messages to detect spam
|
||||
SpamAnalysisResult result = this.spamDetector.AnalyzeMessage(player.getUniqueId(), message, System.currentTimeMillis());
|
||||
|
||||
//prevent bots from chatting - require movement before talking for any newish players
|
||||
//apply any needed changes to message (like lowercasing all-caps)
|
||||
if(event instanceof AsyncPlayerChatEvent)
|
||||
{
|
||||
((AsyncPlayerChatEvent)event).setMessage(result.finalMessage);
|
||||
}
|
||||
|
||||
//don't allow new players to chat after logging in until they move
|
||||
PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId());
|
||||
if(playerData.noChatLocation != null)
|
||||
{
|
||||
|
|
@ -262,8 +264,7 @@ class PlayerEventHandler implements Listener
|
|||
currentLocation.getBlockZ() == playerData.noChatLocation.getBlockZ())
|
||||
{
|
||||
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoChatUntilMove, 10L);
|
||||
spam = true;
|
||||
mutedReason = "pre-movement chat";
|
||||
result.muteReason = "pre-movement chat";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -271,110 +272,18 @@ class PlayerEventHandler implements Listener
|
|||
}
|
||||
}
|
||||
|
||||
//remedy any CAPS SPAM, exception for very short messages which could be emoticons like =D or XD
|
||||
if(message.length() > 4 && this.stringsAreSimilar(message.toUpperCase(), message))
|
||||
{
|
||||
//exception for strings containing forward slash to avoid changing a case-sensitive URL
|
||||
if(event instanceof AsyncPlayerChatEvent)
|
||||
{
|
||||
((AsyncPlayerChatEvent)event).setMessage(message.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
//always mute an exact match to the last chat message
|
||||
long now = new Date().getTime();
|
||||
if(mutedReason != null && message.equals(this.lastChatMessage) && now - this.lastChatMessageTimestamp < 750)
|
||||
{
|
||||
playerData.spamCount += ++this.duplicateMessageCount;
|
||||
spam = true;
|
||||
mutedReason = "repeat message";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.lastChatMessage = message;
|
||||
this.lastChatMessageTimestamp = now;
|
||||
this.duplicateMessageCount = 0;
|
||||
}
|
||||
|
||||
//where other types of spam are concerned, casing isn't significant
|
||||
message = message.toLowerCase();
|
||||
|
||||
//check message content and timing
|
||||
long millisecondsSinceLastMessage = now - playerData.lastMessageTimestamp.getTime();
|
||||
|
||||
//if the message came too close to the last one
|
||||
if(millisecondsSinceLastMessage < 1500)
|
||||
{
|
||||
//increment the spam counter
|
||||
playerData.spamCount++;
|
||||
spam = true;
|
||||
}
|
||||
|
||||
//if it's very similar to the last message from the same player and within 10 seconds of that message
|
||||
if(mutedReason == null && this.stringsAreSimilar(message, playerData.lastMessage) && now - playerData.lastMessageTimestamp.getTime() < 10000)
|
||||
{
|
||||
playerData.spamCount++;
|
||||
spam = true;
|
||||
mutedReason = "similar message";
|
||||
}
|
||||
|
||||
//filter IP addresses
|
||||
if(mutedReason == null)
|
||||
if(result.muteReason == null)
|
||||
{
|
||||
if(GriefPrevention.instance.containsBlockedIP(message))
|
||||
{
|
||||
//spam notation
|
||||
playerData.spamCount+=1;
|
||||
spam = true;
|
||||
|
||||
//block message
|
||||
mutedReason = "IP address";
|
||||
result.muteReason = "IP address";
|
||||
}
|
||||
}
|
||||
|
||||
//if the message was mostly non-alpha-numerics or doesn't include much whitespace, consider it a spam (probably ansi art or random text gibberish)
|
||||
if(mutedReason == null && message.length() > 5)
|
||||
{
|
||||
int symbolsCount = 0;
|
||||
int whitespaceCount = 0;
|
||||
for(int i = 0; i < message.length(); i++)
|
||||
{
|
||||
char character = message.charAt(i);
|
||||
if(!(Character.isLetterOrDigit(character)))
|
||||
{
|
||||
symbolsCount++;
|
||||
}
|
||||
|
||||
if(Character.isWhitespace(character))
|
||||
{
|
||||
whitespaceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(symbolsCount > message.length() / 2 || (message.length() > 15 && whitespaceCount < message.length() / 10))
|
||||
{
|
||||
spam = true;
|
||||
if(playerData.spamCount > 0) mutedReason = "gibberish";
|
||||
playerData.spamCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//very short messages close together are spam
|
||||
if(mutedReason == null && message.length() < 5 && millisecondsSinceLastMessage < 3000)
|
||||
{
|
||||
spam = true;
|
||||
playerData.spamCount++;
|
||||
}
|
||||
|
||||
//in any case, record the timestamp of this message and also its content for next time
|
||||
playerData.lastMessageTimestamp = new Date();
|
||||
playerData.lastMessage = message;
|
||||
|
||||
//if the message was determined to be a spam, consider taking action
|
||||
if(spam)
|
||||
{
|
||||
//anything above level 8 for a player which has received a warning... kick or if enabled, ban
|
||||
if(playerData.spamCount > 8 && playerData.spamWarned)
|
||||
//take action based on spam detector results
|
||||
if(result.shouldBanChatter)
|
||||
{
|
||||
if(GriefPrevention.instance.config_spam_banOffenders)
|
||||
{
|
||||
|
|
@ -394,87 +303,23 @@ class PlayerEventHandler implements Listener
|
|||
PlayerKickBanTask task = new PlayerKickBanTask(player, "", "GriefPrevention Anti-Spam", false);
|
||||
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 1L);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//cancel any messages while at or above the third spam level and issue warnings
|
||||
//anything above level 2, mute and warn
|
||||
if(playerData.spamCount >= 4)
|
||||
{
|
||||
if(mutedReason == null)
|
||||
{
|
||||
mutedReason = "too-frequent text";
|
||||
}
|
||||
if(!playerData.spamWarned)
|
||||
else if(result.shouldWarnChatter)
|
||||
{
|
||||
//warn and log
|
||||
GriefPrevention.sendMessage(player, TextMode.Warn, GriefPrevention.instance.config_spam_warningMessage, 10L);
|
||||
GriefPrevention.AddLogEntry("Warned " + player.getName() + " about spam penalties.", CustomLogEntryTypes.Debug, true);
|
||||
playerData.spamWarned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(mutedReason != null)
|
||||
if(result.muteReason != null)
|
||||
{
|
||||
//make a log entry
|
||||
GriefPrevention.AddLogEntry("Muted " + mutedReason + ".");
|
||||
GriefPrevention.AddLogEntry("Muted " + player.getName() + " " + mutedReason + ":" + message, CustomLogEntryTypes.Debug, true);
|
||||
//mute and log
|
||||
GriefPrevention.AddLogEntry("Muted " + result.muteReason + ".");
|
||||
GriefPrevention.AddLogEntry("Muted " + player.getName() + " " + result.muteReason + ":" + message, CustomLogEntryTypes.Debug, true);
|
||||
|
||||
//cancelling the event guarantees other players don't receive the message
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise if not a spam, reset the spam counter for this player
|
||||
else
|
||||
{
|
||||
playerData.spamCount = 0;
|
||||
playerData.spamWarned = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//if two strings are 75% identical, they're too close to follow each other in the chat
|
||||
private boolean stringsAreSimilar(String message, String lastMessage)
|
||||
{
|
||||
//determine which is shorter
|
||||
String shorterString, longerString;
|
||||
if(lastMessage.length() < message.length())
|
||||
{
|
||||
shorterString = lastMessage;
|
||||
longerString = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
shorterString = message;
|
||||
longerString = lastMessage;
|
||||
}
|
||||
|
||||
if(shorterString.length() <= 5) return shorterString.equals(longerString);
|
||||
|
||||
//set similarity tolerance
|
||||
int maxIdenticalCharacters = longerString.length() - longerString.length() / 4;
|
||||
|
||||
//trivial check on length
|
||||
if(shorterString.length() < maxIdenticalCharacters) return false;
|
||||
|
||||
//compare forward
|
||||
int identicalCount = 0;
|
||||
int i;
|
||||
for(i = 0; i < shorterString.length(); i++)
|
||||
{
|
||||
if(shorterString.charAt(i) == longerString.charAt(i)) identicalCount++;
|
||||
if(identicalCount > maxIdenticalCharacters) return true;
|
||||
}
|
||||
|
||||
//compare backward
|
||||
int j;
|
||||
for(j = 0; j < shorterString.length() - i; j++)
|
||||
{
|
||||
if(shorterString.charAt(shorterString.length() - j - 1) == longerString.charAt(longerString.length() - j - 1)) identicalCount++;
|
||||
if(identicalCount > maxIdenticalCharacters) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
269
src/me/ryanhamshire/GriefPrevention/SpamDetector.java
Normal file
269
src/me/ryanhamshire/GriefPrevention/SpamDetector.java
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
package me.ryanhamshire.GriefPrevention;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
class SpamDetector
|
||||
{
|
||||
//last chat message shown and its timestamp, regardless of who sent it
|
||||
private String lastChatMessage = "";
|
||||
private long lastChatMessageTimestamp = 0;
|
||||
|
||||
//number of identical chat messages in a row
|
||||
private int duplicateMessageCount = 0;
|
||||
|
||||
//data for individual chatters
|
||||
ConcurrentHashMap<UUID, ChatterData> dataStore = new ConcurrentHashMap<UUID, ChatterData>();
|
||||
private ChatterData getChatterData(UUID chatterID)
|
||||
{
|
||||
ChatterData data = this.dataStore.get(chatterID);
|
||||
if(data == null)
|
||||
{
|
||||
data = new ChatterData();
|
||||
this.dataStore.put(chatterID, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
SpamAnalysisResult AnalyzeMessage(UUID chatterID, String message, long timestamp)
|
||||
{
|
||||
SpamAnalysisResult result = new SpamAnalysisResult();
|
||||
result.finalMessage = message;
|
||||
|
||||
//remedy any CAPS SPAM, exception for very short messages which could be emoticons like =D or XD
|
||||
if(message.length() > 4 && this.stringsAreSimilar(message.toUpperCase(), message))
|
||||
{
|
||||
message = message.toLowerCase();
|
||||
result.finalMessage = message;
|
||||
}
|
||||
|
||||
boolean spam = false;
|
||||
ChatterData chatterData = this.getChatterData(chatterID);
|
||||
|
||||
//mute if total volume of text from this player is too high
|
||||
if(message.length() > 50 && chatterData.getTotalRecentLength(timestamp) > 200)
|
||||
{
|
||||
spam = true;
|
||||
result.muteReason = "too much chat sent in 10 seconds";
|
||||
chatterData.spamLevel++;
|
||||
}
|
||||
|
||||
//always mute an exact match to the last chat message
|
||||
if(result.finalMessage.equals(this.lastChatMessage) && timestamp - this.lastChatMessageTimestamp < 2000)
|
||||
{
|
||||
chatterData.spamLevel += ++this.duplicateMessageCount;
|
||||
spam = true;
|
||||
result.muteReason = "repeat message";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.lastChatMessage = message;
|
||||
this.lastChatMessageTimestamp = timestamp;
|
||||
this.duplicateMessageCount = 0;
|
||||
}
|
||||
|
||||
//check message content and timing
|
||||
long millisecondsSinceLastMessage = timestamp - chatterData.lastMessageTimestamp;
|
||||
|
||||
//if the message came too close to the last one
|
||||
if(millisecondsSinceLastMessage < 1500)
|
||||
{
|
||||
//increment the spam counter
|
||||
chatterData.spamLevel++;
|
||||
spam = true;
|
||||
}
|
||||
|
||||
//if it's exactly the same as the last message from the same player and within 30 seconds
|
||||
if(result.muteReason == null && millisecondsSinceLastMessage < 30000 && result.finalMessage.equalsIgnoreCase(chatterData.lastMessage))
|
||||
{
|
||||
chatterData.spamLevel++;
|
||||
spam = true;
|
||||
result.muteReason = "repeat message";
|
||||
}
|
||||
|
||||
//if it's very similar to the last message from the same player and within 10 seconds of that message
|
||||
if(result.muteReason == null && millisecondsSinceLastMessage < 10000 && this.stringsAreSimilar(message.toLowerCase(), chatterData.lastMessage.toLowerCase()))
|
||||
{
|
||||
chatterData.spamLevel++;
|
||||
spam = true;
|
||||
if(chatterData.spamLevel > 2)
|
||||
{
|
||||
result.muteReason = "similar message";
|
||||
}
|
||||
}
|
||||
|
||||
//if the message was mostly non-alpha-numerics or doesn't include much whitespace, consider it a spam (probably ansi art or random text gibberish)
|
||||
if(result.muteReason == null && message.length() > 5)
|
||||
{
|
||||
int symbolsCount = 0;
|
||||
int whitespaceCount = 0;
|
||||
for(int i = 0; i < message.length(); i++)
|
||||
{
|
||||
char character = message.charAt(i);
|
||||
if(!(Character.isLetterOrDigit(character)))
|
||||
{
|
||||
symbolsCount++;
|
||||
}
|
||||
|
||||
if(Character.isWhitespace(character))
|
||||
{
|
||||
whitespaceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(symbolsCount > message.length() / 2 || (message.length() > 15 && whitespaceCount < message.length() / 10))
|
||||
{
|
||||
spam = true;
|
||||
if(chatterData.spamLevel > 0) result.muteReason = "gibberish";
|
||||
chatterData.spamLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
//very short messages close together are spam
|
||||
if(result.muteReason == null && message.length() < 5 && millisecondsSinceLastMessage < 3000)
|
||||
{
|
||||
spam = true;
|
||||
chatterData.spamLevel++;
|
||||
}
|
||||
|
||||
//if the message was determined to be a spam, consider taking action
|
||||
if(spam)
|
||||
{
|
||||
//anything above level 8 for a player which has received a warning... kick or if enabled, ban
|
||||
if(chatterData.spamLevel > 8 && chatterData.spamWarned)
|
||||
{
|
||||
result.shouldBanChatter = true;
|
||||
}
|
||||
|
||||
else if(chatterData.spamLevel >= 4)
|
||||
{
|
||||
if(!chatterData.spamWarned)
|
||||
{
|
||||
chatterData.spamWarned = true;
|
||||
result.shouldWarnChatter = true;
|
||||
}
|
||||
|
||||
if(result.muteReason == null)
|
||||
{
|
||||
result.muteReason = "too-frequent text";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise if not a spam, reduce the spam level for this player
|
||||
else
|
||||
{
|
||||
chatterData.spamLevel = 0;
|
||||
chatterData.spamWarned = false;
|
||||
}
|
||||
|
||||
chatterData.AddMessage(message, timestamp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//if two strings are 75% identical, they're too close to follow each other in the chat
|
||||
private boolean stringsAreSimilar(String message, String lastMessage)
|
||||
{
|
||||
//ignore differences in only punctuation and whitespace
|
||||
message = message.replaceAll("[^\\p{Alpha}]", "");
|
||||
lastMessage = lastMessage.replaceAll("[^\\p{Alpha}]", "");
|
||||
|
||||
//determine which is shorter
|
||||
String shorterString, longerString;
|
||||
if(lastMessage.length() < message.length())
|
||||
{
|
||||
shorterString = lastMessage;
|
||||
longerString = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
shorterString = message;
|
||||
longerString = lastMessage;
|
||||
}
|
||||
|
||||
if(shorterString.length() <= 5) return shorterString.equals(longerString);
|
||||
|
||||
//set similarity tolerance
|
||||
int maxIdenticalCharacters = longerString.length() - longerString.length() / 4;
|
||||
|
||||
//trivial check on length
|
||||
if(shorterString.length() < maxIdenticalCharacters) return false;
|
||||
|
||||
//compare forward
|
||||
int identicalCount = 0;
|
||||
int i;
|
||||
for(i = 0; i < shorterString.length(); i++)
|
||||
{
|
||||
if(shorterString.charAt(i) == longerString.charAt(i)) identicalCount++;
|
||||
if(identicalCount > maxIdenticalCharacters) return true;
|
||||
}
|
||||
|
||||
//compare backward
|
||||
int j;
|
||||
for(j = 0; j < shorterString.length() - i; j++)
|
||||
{
|
||||
if(shorterString.charAt(shorterString.length() - j - 1) == longerString.charAt(longerString.length() - j - 1)) identicalCount++;
|
||||
if(identicalCount > maxIdenticalCharacters) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class SpamAnalysisResult
|
||||
{
|
||||
String finalMessage;
|
||||
boolean shouldWarnChatter = false;
|
||||
boolean shouldBanChatter = false;
|
||||
String muteReason;
|
||||
}
|
||||
|
||||
class ChatterData
|
||||
{
|
||||
public String lastMessage = ""; //the player's last chat message, or slash command complete with parameters
|
||||
public long lastMessageTimestamp; //last time the player sent a chat message or used a monitored slash command
|
||||
public int spamLevel = 0; //number of consecutive "spams"
|
||||
public boolean spamWarned = false; //whether the player has received a warning recently
|
||||
|
||||
//all recent message lengths and their total
|
||||
private ConcurrentLinkedQueue<LengthTimestampPair> recentMessageLengths = new ConcurrentLinkedQueue<LengthTimestampPair>();
|
||||
private int recentTotalLength = 0;
|
||||
|
||||
public void AddMessage(String message, long timestamp)
|
||||
{
|
||||
int length = message.length();
|
||||
this.recentMessageLengths.add(new LengthTimestampPair(length, timestamp));
|
||||
this.recentTotalLength += length;
|
||||
|
||||
this.lastMessage = message;
|
||||
this.lastMessageTimestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getTotalRecentLength(long timestamp)
|
||||
{
|
||||
LengthTimestampPair oldestPair = this.recentMessageLengths.peek();
|
||||
while(oldestPair != null && timestamp - oldestPair.timestamp > 10000)
|
||||
{
|
||||
this.recentMessageLengths.poll();
|
||||
this.recentTotalLength -= oldestPair.length;
|
||||
oldestPair = this.recentMessageLengths.peek();
|
||||
}
|
||||
|
||||
return this.recentTotalLength;
|
||||
}
|
||||
}
|
||||
|
||||
class LengthTimestampPair
|
||||
{
|
||||
public long timestamp;
|
||||
public int length;
|
||||
|
||||
public LengthTimestampPair(int length, long timestamp)
|
||||
{
|
||||
this.length = length;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -81,4 +82,208 @@ public class Tests
|
|||
assertFalse(finder.hasMatch("!asas dfasdf"));
|
||||
assertFalse(finder.hasMatch("?asdfa sdfas df"));
|
||||
}
|
||||
|
||||
private UUID player1 = UUID.fromString("f13c5a98-3777-4659-a111-5617adb7d7fb");
|
||||
private UUID player2 = UUID.fromString("8667ba71-b85a-4004-af54-457a9734eed7");
|
||||
|
||||
@Test
|
||||
public void SpamDetector_BasicChatOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
String message = "Hi, everybody! :)";
|
||||
SpamAnalysisResult result = detector.AnalyzeMessage(player1, message, 1000);
|
||||
assertTrue(result.muteReason == null);
|
||||
assertFalse(result.shouldWarnChatter);
|
||||
assertFalse(result.shouldBanChatter);
|
||||
assertTrue(result.finalMessage.equals(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_CoordinatesOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertTrue(detector.AnalyzeMessage(player1, "1029,2945", 0).muteReason == null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "x1029 z2945", 100000).muteReason == null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "x=1029; y=60; z=2945", 200000).muteReason == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_NumbersOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertTrue(detector.AnalyzeMessage(player1, "25", 0).muteReason == null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "12,234.89", 100000).muteReason == null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "20078", 200000).muteReason == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_RepetitionExact()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
String message = "Hi, everybody! :)";
|
||||
assertFalse(detector.AnalyzeMessage(player1, message, 1000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, message, 28000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_RepetitionExactOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
String message = "Hi, everybody! :)";
|
||||
assertFalse(detector.AnalyzeMessage(player1, message, 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, message, 35000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_Padding()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hacking is really fun guys!! :) 123123123456.12398127498762935", 1000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hacking is really fun guys!! :) 112321523456.1239345498762935", 1000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_Repetition()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 5000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 9000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_TeamRepetition()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player2, "Hi, everybody! :)", 2500).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_TeamRepetitionOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "hi", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player2, "hi", 3000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_Gibberish()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertTrue(detector.AnalyzeMessage(player1, "poiufpoiuasdfpoiuasdfuaufpoiasfopiuasdfpoiuasdufsdf", 1000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player2, "&^%(& (&^%(% (*%#@^ #$&(_||", 3000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_RepetitionOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 12000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_TooFast()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "How's it going? :)", 2000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 3000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 4000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_TooMuchVolume()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Once upon a time there was this guy who wanted to be a hacker. So he started logging into Minecraft servers and threatening to DDOS them.", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Everybody knew that he couldn't be a real hacker, because no real hacker would consider hacking Minecraft to be worth their time, but he didn't understand that even after it was explained to him.", 3000).muteReason != null);
|
||||
|
||||
//start of mute
|
||||
assertTrue(detector.AnalyzeMessage(player1, "After I put him in jail and he wasted half an hour of his time trying to solve the (unsolvable) jail 'puzzle', he offered his services to me in exchange for being let out of jail.", 10000).muteReason != null);
|
||||
|
||||
//forgiven after taking a break
|
||||
assertFalse(detector.AnalyzeMessage(player1, "He promised to DDOS any of my 'rival servers'. So I offered him an opportunity to prove he could do what he said, and I gave him his own IP address from our server logs. Then he disappeared for a while.", 16000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "When he finally came back, I /SoftMuted him and left him in the jail.", 28000).muteReason != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_Caps()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
String message = "OMG I LUFF U KRISTINAAAAAA!";
|
||||
SpamAnalysisResult result = detector.AnalyzeMessage(player1, message, 1000);
|
||||
assertTrue(result.finalMessage.equals(message.toLowerCase()));
|
||||
assertTrue(result.muteReason == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_CapsOK()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
String message = "=D";
|
||||
SpamAnalysisResult result = detector.AnalyzeMessage(player1, message, 1000);
|
||||
assertTrue(result.finalMessage.equals(message));
|
||||
assertTrue(result.muteReason == null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_WarnAndBan()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
|
||||
//allowable noise
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "How's it going? :)", 2000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 3000).muteReason != null);
|
||||
|
||||
//begin mute and warning
|
||||
SpamAnalysisResult result = detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 4000);
|
||||
assertTrue(result.muteReason != null);
|
||||
assertTrue(result.shouldWarnChatter);
|
||||
assertFalse(result.shouldBanChatter);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 5000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 6000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 7000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "How's it going? :)", 8000).muteReason != null);
|
||||
|
||||
//ban
|
||||
result = detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 9000);
|
||||
assertTrue(result.shouldBanChatter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void SpamDetector_Forgiveness()
|
||||
{
|
||||
SpamDetector detector = new SpamDetector();
|
||||
|
||||
//allowable noise
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 1000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "How's it going? :)", 2000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 3000).muteReason != null);
|
||||
|
||||
//start of mutes, and a warning
|
||||
SpamAnalysisResult result = detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 4000);
|
||||
assertTrue(result.muteReason != null);
|
||||
assertTrue(result.shouldWarnChatter);
|
||||
assertFalse(result.shouldBanChatter);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 5000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 6000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 7000).muteReason != null);
|
||||
assertTrue(detector.AnalyzeMessage(player1, "How's it going? :)", 8000).muteReason != null);
|
||||
|
||||
//long delay before next message, not muted anymore
|
||||
result = detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 20000);
|
||||
assertFalse(result.shouldBanChatter);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Hi, everybody! :)", 21000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "How's it going? :)", 22000).muteReason != null);
|
||||
assertFalse(detector.AnalyzeMessage(player1, "Oh how I've missed you all! :)", 23000).muteReason != null);
|
||||
|
||||
//mutes start again, and warning appears again
|
||||
result = detector.AnalyzeMessage(player1, "Why is nobody responding to me??!", 24000);
|
||||
assertTrue(result.muteReason != null);
|
||||
assertTrue(result.shouldWarnChatter);
|
||||
assertFalse(result.shouldBanChatter);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user