diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..7a8c469 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @zedwick diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..5041c25 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,16 @@ +pipeline { + agent { + docker { + image 'maven:3-alpine' + args '-v /root/.m2:/root/.m2' + } + } + stages { + stage('Build') { + steps { + sh 'mvn -B -DskipTests clean package' + archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true + } + } + } +} diff --git a/README b/README index 8d70812..a0af973 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ ########################################## ## ## ## Bungee Chat Filter Config ## -## v1.2 ## +## v1.7 ## ########################################## #Monitor commands - Monitor commands send by the players as chat @@ -11,6 +11,7 @@ Monitor Commands: true Commands: - msg - message + - m - tell - whisper - w @@ -19,35 +20,49 @@ Commands: - s - l - local + - url + - website - hub + - shop + - broadcast #AntiSpam - Will use the minimum chat delay to force players to wait *1500ms or what ever you specified between messages AntiSpam: true +AntiRepeat: true #Minimum-Chat-Delay - The time in miliseconds a player must wait between messages Minimum-Chat-Delay: 1500 +Minimum-Repeat-Delay: 60000 +#Messages sent to the player in chat when they are blocked by AntiSpam or AntiRepeat +AntiSpamMessage: &cPlease do not spam +AntiRepeatMessage: &cPlease do not spam #Rules - Groups of rules which monitor the chat #rules: -# rulename: - the name of the rule -# regex: - the string regex that the rule will check for -# ignores: - If the message contains a value that matches ignore then none of the actions will be performed -# permission: - the permission required to bypass this rule -# actions: - here is the list of actions the rule will perform if matched -# deny: true - this will deny the message and cancel the event -# message: (message) -this will send a message (message) to the player -# kick: (message) - this will kick the player with the (message) -# alert: (message) - this will send a broadcast to the server {player} will be replaced with the players display name -# command: /(command) - this will cause the player to send the (command) -# remove: true - this will remove any matches from the players message -# replace: - this will replace the matched word with a random word from the list below +# rulename: - the name of the rule. +# regex: - the string regex that the rule will check for. +# ignores: - If the message contains a value that matches ignore then none of the actions will be performed. +# permission: - the permission required to bypass this rule. +# actions: - here is the list of actions the rule will perform if matched. +# deny: true - this will deny the message and cancel the event. +# message: - this will send a message - this will kick the player with the . (Supports Placeholders, see below) +# alert: - this will send the to all players connected to the proxy server. (Supports Placeholders, see below) +# remove: true - this will remove any matches from the player's message. +# replace: - this will replace the matched word with a random word from the list below. (Supports Placeholders, see below) # - word1 # - word2 -# lower: - this will change any matches into lowercase -# pcommand: /(command) - Proxy command, this will cause the player to send the (command) to the proxy server -# scommand: /(command) - Server command this will cause the player to send the (command) to their current server -# ccommand: /(command) - Console command, this will cause the proxy server console to execute the (command). +# lower: true - this will change any matches into lowercase. +# pcommand: (command) - Proxy command, this will cause the player to send the (command) to the proxy server. Do not include a /. (Supports Placeholders, see below) +# scommand: /(command) - Server command this will cause the player to send the (command) to their current server. You must include a /. (Supports Placeholders, see below) +# ccommand: (command) - Console command, this will cause the proxy server console to execute the (command). Do not include a /. (Supports Placeholders, see below) # Note that there is currently no way to execute a command as the current server console. +#Placeholders - These placeholders can be used in any action strings. +# +# {player} - Replaced with the display name of the player sending the message +# {message} - Replaced with the original message as sent by the player +# {arguments} - Intended for use with commands, this will be replaced by all of the arguments following the first word of the player's message. +# (for example a player sending "/send Zedwick Hello!" will return "Zedwick Hello!" in place of {arguments}) rules: @@ -155,7 +170,10 @@ rules: regex: /broadcast permission: "!bungeefilter.broadcast" actions: - alert: '&5[Broadcast]&a{player}&f:{arguments}' + alert: + - '' + - '&5[Broadcast] &a{player}&f: {arguments}' + - '' deny: true #Shortcut for website URL can use either /url or /website @@ -163,4 +181,4 @@ rules: regex: (/url)|(/website) actions: message: "&l&6[Check it out]&3 This server's website is &6www.minecraftserver.com" - deny: true \ No newline at end of file + deny: true diff --git a/pom.xml b/pom.xml index b4b382b..cc165dc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ BungeeChatFilter BungeeChatFilter - 1.5-SNAPSHOT + 1.8 jar BungeeChatFilter @@ -40,7 +40,7 @@ - ${project.name} + ${project.name}-${project.version} ${project.basedir}/src/main/resources @@ -56,8 +56,8 @@ maven-compiler-plugin 3.1 - 1.7 - 1.7 + 1.8 + 1.8 true true diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/BFReload.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/BFReload.java index af3d0c2..228a87a 100644 --- a/src/main/java/com/minecraftdimensions/bungeechatfilter/BFReload.java +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/BFReload.java @@ -2,6 +2,7 @@ import com.minecraftdimensions.bungeechatfilter.configlibrary.Config; import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Command; import java.util.ArrayList; @@ -30,6 +31,6 @@ public void execute( CommandSender sender, String[] args ) { Main.COMLIST = Main.c.getListString( "Commands", defaultList ); Main.NOSPAM = Main.c.getBoolean( "AntiSpam", true ); Main.loadRules(); - sender.sendMessage( "BungeeFilter Reloaded" ); + sender.sendMessage(new TextComponent( "BungeeFilter Reloaded" )); } } diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/Main.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/Main.java index 6ebe7d3..ae5abed 100644 --- a/src/main/java/com/minecraftdimensions/bungeechatfilter/Main.java +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/Main.java @@ -12,6 +12,7 @@ public class Main extends Plugin { public static long SPAMTIMER = 0; + public static long REPEATTIMER = 0; public static Boolean COMMANDS; public static List COMLIST; public static ArrayList RULES; @@ -69,7 +70,8 @@ private void initialiseConfig() { COMLIST = c.getListString( "Commands", defaultList ); NOSPAM = c.getBoolean( "AntiSpam", true ); NOREPEAT = c.getBoolean( "AntiRepeat", true ); - SPAMTIMER = c.getInt( "Minimum-Chat-Delay" ) ; + SPAMTIMER = c.getInt( "Minimum-Chat-Delay", 1500 ) ; + REPEATTIMER = c.getInt( "Minimum-Repeat-Delay", 60000 ) ; loadRules(); } @@ -89,13 +91,20 @@ public static void loadRules() { } String perm = c.getString( "rules." + node + ".permission" ); String ignore = c.getString( "rules." + node + ".ignores" ); - HashMap actions = new HashMap<>(); + HashMap actions = new HashMap<>(); for ( String action : c.getSubNodes( "rules." + node + ".actions" ) ) { + Object obj = c.get( "rules." + node + ".actions." + action ); if ( action.equals( "replace" ) ) { List strlist = c.getListString( "rules." + node + ".actions.replace" ); actions.put( action, strlist.toArray( new String[strlist.size()] ) ); + } else if ( obj instanceof List ) { + actions.put( action, c.getListString("rules." + node + ".actions." + action ) ); + } else if ( obj instanceof String ) { + List stringList = new ArrayList(); + stringList.add( c.getString("rules." + node + ".actions." + action ) ); + actions.put( action, stringList ); } else { - actions.put( action, new String[] { c.getString( "rules." + node + ".actions." + action ) } ); + actions.put( action, c.get( "rules." + node + ".actions." + action ) ); } } RULES.add( new Rule( regex, actions, perm, ignore ) ); @@ -104,7 +113,4 @@ public static void loadRules() { } - public String color( String s ) { - return ChatColor.translateAlternateColorCodes( '&', s ); - } } diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/PlayerChatListener.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/PlayerChatListener.java index e11cce3..f58ca1b 100644 --- a/src/main/java/com/minecraftdimensions/bungeechatfilter/PlayerChatListener.java +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/PlayerChatListener.java @@ -17,33 +17,34 @@ public void playerChat( ChatEvent e ) { if ( !player.hasPermission( "bungeefilter.bypass" ) ) { if ( !Main.COMMANDS && isChatCommand( e.getMessage() ) ) { return; + } else if( Main.COMMANDS && isChatCommand( e.getMessage() ) && !isMonitoredCommand( e.getMessage() )){ + return; } - if(Main.NOREPEAT){ - if(repeatCheck(player.getName(), e.getMessage())){ + if ( Main.NOSPAM && !player.hasPermission( "bungeefilter.bypass.spam" ) ) { + if ( spamCheck( player, e.getMessage(), System.currentTimeMillis()) ) { e.setCancelled( true ); - player.sendMessage( new TextComponent( ChatColor.RED + "Please do not spam" ) ); + player.sendMessage( new TextComponent( TextComponent.fromLegacyText(util.color( Main.c.getString("AntiSpamMessage", "&cPlease do not spam") ) ) ) ); return; - }else{ - Main.ANTIREPEAT.put( player.getName(), e.getMessage() ); } - } - if ( Main.NOSPAM ) { - if ( spamCheck( player, e.getMessage(), System.currentTimeMillis()) ) { + if(Main.NOREPEAT && !player.hasPermission( "bungeefilter.bypass.repeat" )){ + if(repeatCheck(player.getName(), e.getMessage(),System.currentTimeMillis())){ e.setCancelled( true ); - player.sendMessage( new TextComponent( ChatColor.RED + "Please do not spam" ) ); + player.sendMessage( new TextComponent( TextComponent.fromLegacyText(util.color( Main.c.getString("AntiRepeatMessage", "&cPlease do not spam") ) ) ) ); return; - } else { - Main.ANTISPAM.put( player.getName(),System.currentTimeMillis()); + }else{ + Main.ANTIREPEAT.put( player.getName(), e.getMessage() ); } + } + Main.ANTISPAM.put( player.getName(),System.currentTimeMillis()); for ( Rule r : Main.RULES ) { if(r.hasPermission()){ if(!r.needsPerm && player.hasPermission( r.getPermission() )){ - return; + continue; } if(r.needsPerm && !player.hasPermission( r.getPermission() )){ - return; + continue; } } if ( r.doesMessageContainRegex( e.getMessage() ) ) { @@ -55,31 +56,31 @@ public void playerChat( ChatEvent e ) { } - private boolean repeatCheck( String name, String message ) { - if(isChatCommand( message ) && !isMonitoredCommand( message )){ - return false; - } - if ( Main.ANTIREPEAT.containsKey( name ) ) { - return Main.ANTIREPEAT.get( name ).equals( message ); - } - return false; - } - private boolean spamCheck( ProxiedPlayer player,String message, long time ) { - if(isChatCommand( message ) && !isMonitoredCommand( message )){ - return false; - } if ( Main.ANTISPAM.containsKey( player.getName() ) ) { Long diff = time-Main.ANTISPAM.get( player.getName() ); return diff actions; + HashMap actions; String permission = null; boolean needsPerm; - public Rule( String regex, HashMap actions, String permission, String ignores ) { - this.regex = Pattern.compile( regex ); + public Rule( String regex, HashMap actions, String permission, String ignores ) { + this.regex = Pattern.compile( regex , Pattern.UNICODE_CHARACTER_CLASS); if ( ignores == null ) { ignore = null; } else{ - this.ignore = Pattern.compile( ignores ); + this.ignore = Pattern.compile( ignores , Pattern.UNICODE_CHARACTER_CLASS); } this.actions = actions; if(permission!=null && permission.startsWith( "!" )){ @@ -58,40 +62,45 @@ public void performActions( ChatEvent event, ProxiedPlayer player ) { } } for ( String action : actions.keySet() ) { - if ( action.equals( "deny" ) ) { + if ( action.equals( "deny" ) && (Boolean) actions.get( action ) ) { event.setCancelled( true ); } else if ( action.equals( "message" ) ) { - player.sendMessage( color( actions.get( action )[0] ) ); + player.sendMessage( util.MessageFromStringList( (List) actions.get( action ), event ) ); } else if ( action.equals( "kick" ) ) { - player.disconnect( color( actions.get( action )[0] ) ); + player.disconnect( util.MessageFromStringList( (List) actions.get( action ), event ) ); } else if ( action.equals( "alert" ) ) { - String alert = actions.get( action )[0].replace( "{player}", player.getDisplayName() ); - if(message.split( " ", 2 ).length>1){ - alert =alert.replace("{arguments}", message.split( " ", 2 )[1] ) ; - } - ProxyServer.getInstance().broadcast( color( alert )); + ProxyServer.getInstance().broadcast( util.MessageFromStringList( (List) actions.get( action ), event ) ); } else if ( action.equals( "scommand" ) ) { - player.chat( actions.get( action )[0] ); + for ( String scommand : (List) actions.get( action ) ) { + player.chat( util.ParseVariables( scommand, event ) ); + } } else if ( action.equals( "pcommand" ) ) { - ProxyServer.getInstance().getPluginManager().dispatchCommand( player, actions.get( action )[0] ); - } else if( action.equals( "ccommand" )){ - ProxyServer.getInstance().getPluginManager().dispatchCommand( ProxyServer.getInstance().getConsole(), actions.get( action )[0].replace( "{player}", player.getName() ) ); - } else if ( action.equals( "remove" ) ) { + for ( String pcommand : (List) actions.get( action ) ) { + ProxyServer.getInstance().getPluginManager().dispatchCommand(player, util.ParseVariables(pcommand, event) ); + } + } else if( action.equals( "ccommand" ) ) { + for ( String ccommand : (List) actions.get( action ) ) { + ProxyServer.getInstance().getPluginManager().dispatchCommand( ProxyServer.getInstance().getConsole(), util.ParseVariables(ccommand, event) ); + } + } else if ( action.equals( "remove" ) && (Boolean) actions.get( action ) ) { message = message.replaceAll( regex.pattern(), "" ); } else if ( action.equals( "replace" ) ) { Random rand = new Random(); Matcher m = getMatcher( message ); StringBuilder sb = new StringBuilder(); int last = 0; - while ( m.find() ) { - int n = rand.nextInt( actions.get( action ).length ); - sb.append( message.substring( last, m.start() ) ); - sb.append( actions.get( action )[n] ); - last = m.end(); + String[] replacements = (String[]) actions.get( action ); + if ( replacements.length > 0 ){ + while ( m.find() ) { + int n = rand.nextInt( replacements.length ); + sb.append( message.substring( last, m.start() ) ); + sb.append( util.ParseVariables(((String[]) actions.get( action ))[n], event) ); + last = m.end(); + } } sb.append( message.substring( last ) ); message = sb.toString(); - } else if ( action.equals( "lower" ) ) { + } else if ( action.equals( "lower" ) && (Boolean) actions.get( action ) ) { Matcher m = getMatcher( message ); StringBuilder sb = new StringBuilder(); int last = 0; @@ -107,10 +116,6 @@ public void performActions( ChatEvent event, ProxiedPlayer player ) { event.setMessage( message ); } - public String color( String s ) { - return ChatColor.translateAlternateColorCodes( '&', s ); - } - public boolean hasPermission() { return permission != null; } diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/Config.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/Config.java index 32051f3..c8ecc65 100644 --- a/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/Config.java +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/Config.java @@ -49,6 +49,15 @@ public void createFile() { } } } + + public Object get( String key ) { + + if ( fconfig.contains( key ) ) { + return fconfig.get( key ); + } else{ + return null; + } + } public String getString( String key, String def ) { diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/FileConfiguration.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/FileConfiguration.java index 73437a9..7942753 100644 --- a/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/FileConfiguration.java +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/configlibrary/FileConfiguration.java @@ -1,6 +1,7 @@ package com.minecraftdimensions.bungeechatfilter.configlibrary; import java.io.*; +import java.nio.charset.StandardCharsets; public abstract class FileConfiguration extends MemoryConfiguration { public FileConfiguration() { @@ -38,7 +39,7 @@ public void load( File file ) throws FileNotFoundException, IOException, Invalid public void load( InputStream stream ) throws IOException, InvalidConfigurationException { - InputStreamReader reader = new InputStreamReader( stream ); + InputStreamReader reader = new InputStreamReader( stream , StandardCharsets.UTF_8 ); StringBuilder builder = new StringBuilder(); BufferedReader input = new BufferedReader( reader ); diff --git a/src/main/java/com/minecraftdimensions/bungeechatfilter/util.java b/src/main/java/com/minecraftdimensions/bungeechatfilter/util.java new file mode 100644 index 0000000..65b5c05 --- /dev/null +++ b/src/main/java/com/minecraftdimensions/bungeechatfilter/util.java @@ -0,0 +1,64 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.minecraftdimensions.bungeechatfilter; + +import java.util.Iterator; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.Connection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ChatEvent; + +/** + * + * @author zedwick + */ +public class util { + + public static String color(String s) { + return ChatColor.translateAlternateColorCodes('&', s); + } + public static TextComponent MessageFromStringList(List StringList, ChatEvent event) { + TextComponent message = new TextComponent(); + + for (Iterator it = StringList.iterator(); it.hasNext();) { + String string = it.next(); + string = ParseVariables(string, event); + String newLine = ""; + if ( it.hasNext() ) { + newLine = "\n"; + } + message.addExtra(new TextComponent(StringToBaseComponent(string+newLine))); + } + + return message; + } + + public static BaseComponent[] StringToBaseComponent(String string) { + return TextComponent.fromLegacyText(color(string)); + } + + public static String ParseVariables(String string, ChatEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + String message = event.getMessage(); + String arguments = ""; + + if(message.split( " ", 2 ).length>1) { + arguments = message.split( " ", 2 )[1]; + } + + string = string + .replace("{player}", player.getDisplayName()) + .replace("{message}", message) + .replace("{arguments}", arguments); + + + + return string; + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8d70812..a14b19d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,7 @@ ########################################## ## ## ## Bungee Chat Filter Config ## -## v1.2 ## +## v1.7 ## ########################################## #Monitor commands - Monitor commands send by the players as chat @@ -11,6 +11,7 @@ Monitor Commands: true Commands: - msg - message + - m - tell - whisper - w @@ -19,35 +20,49 @@ Commands: - s - l - local + - url + - website - hub + - shop + - broadcast #AntiSpam - Will use the minimum chat delay to force players to wait *1500ms or what ever you specified between messages AntiSpam: true +AntiRepeat: true #Minimum-Chat-Delay - The time in miliseconds a player must wait between messages Minimum-Chat-Delay: 1500 +Minimum-Repeat-Delay: 60000 +#Messages sent to the player in chat when they are blocked by AntiSpam or AntiRepeat +AntiSpamMessage: &cPlease do not spam +AntiRepeatMessage: &cPlease do not spam #Rules - Groups of rules which monitor the chat #rules: -# rulename: - the name of the rule -# regex: - the string regex that the rule will check for -# ignores: - If the message contains a value that matches ignore then none of the actions will be performed -# permission: - the permission required to bypass this rule -# actions: - here is the list of actions the rule will perform if matched -# deny: true - this will deny the message and cancel the event -# message: (message) -this will send a message (message) to the player -# kick: (message) - this will kick the player with the (message) -# alert: (message) - this will send a broadcast to the server {player} will be replaced with the players display name -# command: /(command) - this will cause the player to send the (command) -# remove: true - this will remove any matches from the players message -# replace: - this will replace the matched word with a random word from the list below +# rulename: - the name of the rule. +# regex: - the string regex that the rule will check for. +# ignores: - If the message contains a value that matches ignore then none of the actions will be performed. +# permission: - the permission required to bypass this rule. +# actions: - here is the list of actions the rule will perform if matched. +# deny: true - this will deny the message and cancel the event. +# message: - this will send a message - this will kick the player with the . (Supports Placeholders, see below) +# alert: - this will send the to all players connected to the proxy server. (Supports Placeholders, see below) +# remove: true - this will remove any matches from the player's message. +# replace: - this will replace the matched word with a random word from the list below. (Supports Placeholders, see below) # - word1 # - word2 -# lower: - this will change any matches into lowercase -# pcommand: /(command) - Proxy command, this will cause the player to send the (command) to the proxy server -# scommand: /(command) - Server command this will cause the player to send the (command) to their current server -# ccommand: /(command) - Console command, this will cause the proxy server console to execute the (command). +# lower: true - this will change any matches into lowercase. +# pcommand: (command) - Proxy command, this will cause the player to send the (command) to the proxy server. Do not include a /. (Supports Placeholders, see below) +# scommand: /(command) - Server command this will cause the player to send the (command) to their current server. You must include a /. (Supports Placeholders, see below) +# ccommand: (command) - Console command, this will cause the proxy server console to execute the (command). Do not include a /. (Supports Placeholders, see below) # Note that there is currently no way to execute a command as the current server console. +#Placeholders - These placeholders can be used in any action strings. +# +# {player} - Replaced with the display name of the player sending the message +# {message} - Replaced with the original message as sent by the player +# {arguments} - Intended for use with commands, this will be replaced by all of the arguments following the first word of the player's message. +# (for example a player sending "/send Zedwick Hello!" will return "Zedwick Hello!" in place of {arguments}) rules: @@ -72,8 +87,7 @@ rules: - queer - bitch - bastard - - dick - - gay) + - dick) actions: replace: - lovely @@ -140,7 +154,7 @@ rules: CommandShortcut: regex: /hub actions: - pcommand: /server hub + pcommand: server hub deny: true #When a player uses the command /shop they will instead send a command to the server they are on /warp shop. @@ -155,7 +169,10 @@ rules: regex: /broadcast permission: "!bungeefilter.broadcast" actions: - alert: '&5[Broadcast]&a{player}&f:{arguments}' + alert: + - '' + - '&5[Broadcast] &a{player}&f: {arguments}' + - '' deny: true #Shortcut for website URL can use either /url or /website diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7c4297e..5a5a882 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,6 @@ name: BungeeChatFilter main : com.minecraftdimensions.bungeechatfilter.Main version: ${project.version}b-${build.number} -author: Bloodsplat \ No newline at end of file +author: Bloodsplat, Zedwick +website: https://www.spigotmc.org/resources/bungeechatfilter.20596/ +description: Chat and command filter control for BungeeCord using regex