Added profanity/troll filter.

This commit is contained in:
ryanhamshire 2015-09-17 19:14:36 -07:00
parent 2a66ad3d01
commit f5318e3b19
6 changed files with 157 additions and 1 deletions

View File

@ -19,6 +19,7 @@
package me.ryanhamshire.GriefPrevention; package me.ryanhamshire.GriefPrevention;
import java.io.*; import java.io.*;
import java.nio.charset.Charset;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -61,6 +62,7 @@ public abstract class DataStore
final static String configFilePath = dataLayerFolderPath + File.separator + "config.yml"; final static String configFilePath = dataLayerFolderPath + File.separator + "config.yml";
final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml"; final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml";
final static String softMuteFilePath = dataLayerFolderPath + File.separator + "softMute.txt"; final static String softMuteFilePath = dataLayerFolderPath + File.separator + "softMute.txt";
final static String bannedWordsFilePath = dataLayerFolderPath + File.separator + "bannedWords.txt";
//the latest version of the data schema implemented here //the latest version of the data schema implemented here
protected static final int latestSchemaVersion = 2; protected static final int latestSchemaVersion = 2;
@ -211,6 +213,31 @@ public abstract class DataStore
} }
} }
List<String> loadBannedWords()
{
try
{
File bannedWordsFile = new File(bannedWordsFilePath);
if(!bannedWordsFile.exists())
{
Files.touch(bannedWordsFile);
String defaultWords =
"nigger\nniggers\nniger\nnigga\nnigers\nniggas\n" +
"fag\nfags\nfaggot\nfaggots\nfeggit\nfeggits\nfaggit\nfaggits\n" +
"cunt\ncunts\nwhore\nwhores\nslut\nsluts\n";
Files.append(defaultWords, bannedWordsFile, Charset.forName("UTF-8"));
}
return Files.readLines(bannedWordsFile, Charset.forName("UTF-8"));
}
catch(Exception e)
{
GriefPrevention.AddLogEntry("Failed to read from the banned words data file: " + e.toString());
e.printStackTrace();
return new ArrayList<String>();
}
}
//updates soft mute map and data file //updates soft mute map and data file
boolean toggleSoftMute(UUID playerID) boolean toggleSoftMute(UUID playerID)
{ {
@ -1338,6 +1365,7 @@ public abstract class DataStore
this.addDefault(defaults, Messages.BookTools, "Our claim tools are {0} and {1}.", "0: claim modification tool name; 1:claim information tool name"); this.addDefault(defaults, Messages.BookTools, "Our claim tools are {0} and {1}.", "0: claim modification tool name; 1:claim information tool name");
this.addDefault(defaults, Messages.BookDisabledChestClaims, " On this server, placing a chest will NOT claim land for you.", null); this.addDefault(defaults, Messages.BookDisabledChestClaims, " On this server, placing a chest will NOT claim land for you.", null);
this.addDefault(defaults, Messages.BookUsefulCommands, "Useful Commands:", null); this.addDefault(defaults, Messages.BookUsefulCommands, "Useful Commands:", null);
this.addDefault(defaults, Messages.NoProfanity, "Please moderate your language.", null);
//load the config file //load the config file
FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath));

View File

@ -20,5 +20,5 @@ package me.ryanhamshire.GriefPrevention;
public enum Messages public enum Messages
{ {
RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound2, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionVideo2, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsVideo2, SurvivalBasicsVideo2, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooNarrow, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime, BuildingOutsideClaims, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured, NoTeleportPvPCombat, NoTNTDamageAboveSeaLevel, NoTNTDamageClaims, IgnoreClaimsAdvertisement, NoPermissionForCommand, ClaimsListNoPermission, ExplosivesDisabled, ExplosivesEnabled, ClaimExplosivesAdvertisement, PlayerInPvPSafeZone, NoPistonsOutsideClaims, SoftMuted, UnSoftMuted, DropUnlockAdvertisement, PickupBlockedExplanation, DropUnlockConfirmation, AdvertiseACandACB, AdvertiseAdminClaims, AdvertiseACB, NotYourPet, PetGiveawayConfirmation, PetTransferCancellation, ReadyToTransferPet, AvoidGriefClaimLand, BecomeMayor, ClaimCreationFailedOverClaimCountLimit, CreateClaimFailOverlapRegion, ResizeFailOverlapRegion, NoBuildPortalPermission, ShowNearbyClaims, NoChatUntilMove, SiegeImmune, SetClaimBlocksSuccess, IgnoreConfirmation, NotIgnoringPlayer, UnIgnoreConfirmation, SeparateConfirmation, UnSeparateConfirmation, NotIgnoringAnyone, TrustListHeader, Manage, Build, Containers, Access, StartBlockMath, ClaimsListHeader, ContinueBlockMath, EndBlockMath, NoClaimDuringPvP, UntrustAllOwnerOnly, ManagersDontUntrustManagers, BookAuthor, BookTitle, BookIntro, BookDisabledChestClaims, BookUsefulCommands, BookLink, BookTools, ResizeClaimTooNarrow, ResizeClaimInsufficientArea RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound2, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionVideo2, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsVideo2, SurvivalBasicsVideo2, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooNarrow, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime, BuildingOutsideClaims, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured, NoTeleportPvPCombat, NoTNTDamageAboveSeaLevel, NoTNTDamageClaims, IgnoreClaimsAdvertisement, NoPermissionForCommand, ClaimsListNoPermission, ExplosivesDisabled, ExplosivesEnabled, ClaimExplosivesAdvertisement, PlayerInPvPSafeZone, NoPistonsOutsideClaims, SoftMuted, UnSoftMuted, DropUnlockAdvertisement, PickupBlockedExplanation, DropUnlockConfirmation, AdvertiseACandACB, AdvertiseAdminClaims, AdvertiseACB, NotYourPet, PetGiveawayConfirmation, PetTransferCancellation, ReadyToTransferPet, AvoidGriefClaimLand, BecomeMayor, ClaimCreationFailedOverClaimCountLimit, CreateClaimFailOverlapRegion, ResizeFailOverlapRegion, NoBuildPortalPermission, ShowNearbyClaims, NoChatUntilMove, SiegeImmune, SetClaimBlocksSuccess, IgnoreConfirmation, NotIgnoringPlayer, UnIgnoreConfirmation, SeparateConfirmation, UnSeparateConfirmation, NotIgnoringAnyone, TrustListHeader, Manage, Build, Containers, Access, StartBlockMath, ClaimsListHeader, ContinueBlockMath, EndBlockMath, NoClaimDuringPvP, UntrustAllOwnerOnly, ManagersDontUntrustManagers, BookAuthor, BookTitle, BookIntro, BookDisabledChestClaims, BookUsefulCommands, BookLink, BookTools, ResizeClaimTooNarrow, ResizeClaimInsufficientArea, NoProfanity
} }

View File

@ -144,6 +144,9 @@ public class PlayerData
public ConcurrentHashMap<UUID, Boolean> ignoredPlayers = new ConcurrentHashMap<UUID, Boolean>(); public ConcurrentHashMap<UUID, Boolean> ignoredPlayers = new ConcurrentHashMap<UUID, Boolean>();
public boolean ignoreListChanged = false; public boolean ignoreListChanged = false;
//profanity warning, once per play session
boolean profanityWarned = false;
//whether or not this player is "in" pvp combat //whether or not this player is "in" pvp combat
public boolean inPvpCombat() public boolean inPvpCombat()
{ {

View File

@ -87,6 +87,9 @@ class PlayerEventHandler implements Listener
//regex pattern for the "how do i claim land?" scanner //regex pattern for the "how do i claim land?" scanner
private Pattern howToClaimPattern = null; private Pattern howToClaimPattern = null;
//matcher for banned words
private WordFinder bannedWordFinder = new WordFinder(GriefPrevention.instance.dataStore.loadBannedWords());
//typical constructor, yawn //typical constructor, yawn
PlayerEventHandler(DataStore dataStore, GriefPrevention plugin) PlayerEventHandler(DataStore dataStore, GriefPrevention plugin)
{ {
@ -138,6 +141,34 @@ class PlayerEventHandler implements Listener
GriefPrevention.AddLogEntry(notificationMessage, CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry(notificationMessage, CustomLogEntryTypes.Debug, true);
} }
//troll and excessive profanity filter
else if(!player.hasPermission("griefprevention.spam") && this.bannedWordFinder.hasMatch(message))
{
//limit recipients to sender
recipients.clear();
recipients.add(player);
//if player not new warn for the first infraction per play session.
if(player.hasAchievement(Achievement.MINE_WOOD))
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
if(!playerData.profanityWarned)
{
playerData.profanityWarned = true;
GriefPrevention.sendMessage(player, TextMode.Err, Messages.NoProfanity);
event.setCancelled(true);
return;
}
}
//otherwise assume chat troll and mute all chat from this sender until an admin says otherwise
else
{
GriefPrevention.AddLogEntry("Auto-muted new player " + player.getName() + " for profanity shortly after join. Use /SoftMute to undo.");
GriefPrevention.instance.dataStore.toggleSoftMute(player.getUniqueId());
}
}
//remaining messages //remaining messages
else else
{ {

View File

@ -0,0 +1,60 @@
package me.ryanhamshire.GriefPrevention;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
public class Tests
{
@Test
public void TrivialTest()
{
assertTrue(true);
}
@Test
public void WordFinder_BeginningMiddleEnd()
{
WordFinder finder = new WordFinder(Arrays.asList("alpha", "beta", "gamma"));
assertTrue(finder.hasMatch("alpha"));
assertTrue(finder.hasMatch("alpha etc"));
assertTrue(finder.hasMatch("etc alpha etc"));
assertTrue(finder.hasMatch("etc alpha"));
assertTrue(finder.hasMatch("beta"));
assertTrue(finder.hasMatch("beta etc"));
assertTrue(finder.hasMatch("etc beta etc"));
assertTrue(finder.hasMatch("etc beta"));
assertTrue(finder.hasMatch("gamma"));
assertTrue(finder.hasMatch("gamma etc"));
assertTrue(finder.hasMatch("etc gamma etc"));
assertTrue(finder.hasMatch("etc gamma"));
}
@Test
public void WordFinder_Casing()
{
WordFinder finder = new WordFinder(Arrays.asList("aLPhA"));
assertTrue(finder.hasMatch("alpha"));
assertTrue(finder.hasMatch("aLPhA"));
assertTrue(finder.hasMatch("AlpHa"));
assertTrue(finder.hasMatch("ALPHA"));
}
@Test
public void WordFinder_Punctuation()
{
WordFinder finder = new WordFinder(Arrays.asList("alpha"));
assertTrue(finder.hasMatch("What do you think,alpha?"));
}
@Test
public void WordFinder_NoMatch()
{
WordFinder finder = new WordFinder(Arrays.asList("alpha"));
assertFalse(finder.hasMatch("Unit testing is smart."));
}
}

View File

@ -0,0 +1,34 @@
package me.ryanhamshire.GriefPrevention;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class WordFinder
{
private Pattern pattern;
WordFinder(List<String> wordsToFind)
{
StringBuilder patternBuilder = new StringBuilder();
for(String word : wordsToFind)
{
patternBuilder.append("|(([^\\w]|^)" + Pattern.quote(word) + "([^\\w]|$))");
}
String patternString = patternBuilder.toString();
if(patternString.length() > 1)
{
//trim extraneous leading pipe (|)
patternString = patternString.substring(1);
}
this.pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
}
boolean hasMatch(String input)
{
Matcher matcher = this.pattern.matcher(input);
return matcher.find();
}
}