From 16d5b4a5a28d21aaa6bd5a598fd4c55a1964cb2c Mon Sep 17 00:00:00 2001 From: ImproperIssues Date: Sat, 15 Apr 2023 15:31:08 -0700 Subject: [PATCH] config command --- .../ogredupealias/OgreDupeAlias.java | 10 ++ .../commands/commands/MuteChatCommand.java | 43 +++++ .../commands/commands/StaffChatCommand.java | 41 +++++ .../itzispyder/ogredupealias/data/Config.java | 5 + .../events/ChatEventListener.java | 19 ++ .../events/PlayerEventListener.java | 26 +++ .../ogredupealias/utils/ArrayUtils.java | 11 ++ .../ogredupealias/utils/ChatConstraints.java | 168 ++++++++++++++++++ .../ogredupealias/utils/ServerUtils.java | 47 +++++ src/main/resources/config.yml | 5 +- src/main/resources/plugin.yml | 48 ++++- 11 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/commands/commands/MuteChatCommand.java create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/commands/commands/StaffChatCommand.java create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/events/ChatEventListener.java create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/events/PlayerEventListener.java create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/utils/ChatConstraints.java create mode 100644 src/main/java/io/github/itzispyder/ogredupealias/utils/ServerUtils.java diff --git a/src/main/java/io/github/itzispyder/ogredupealias/OgreDupeAlias.java b/src/main/java/io/github/itzispyder/ogredupealias/OgreDupeAlias.java index 01fb3f5..62b8d87 100644 --- a/src/main/java/io/github/itzispyder/ogredupealias/OgreDupeAlias.java +++ b/src/main/java/io/github/itzispyder/ogredupealias/OgreDupeAlias.java @@ -1,7 +1,11 @@ package io.github.itzispyder.ogredupealias; import io.github.itzispyder.ogredupealias.commands.commands.ConfigCommand; +import io.github.itzispyder.ogredupealias.commands.commands.MuteChatCommand; +import io.github.itzispyder.ogredupealias.commands.commands.StaffChatCommand; import io.github.itzispyder.ogredupealias.data.Config; +import io.github.itzispyder.ogredupealias.events.ChatEventListener; +import io.github.itzispyder.ogredupealias.events.PlayerEventListener; import org.bukkit.Bukkit; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -31,10 +35,16 @@ public final class OgreDupeAlias extends JavaPlugin { public void init() { // Events + pm.registerEvents(new ChatEventListener(),this); + pm.registerEvents(new PlayerEventListener(),this); // Commands getCommand("config").setExecutor(new ConfigCommand()); getCommand("config").setTabCompleter(new ConfigCommand()); + getCommand("mutechat").setExecutor(new MuteChatCommand()); + getCommand("mutechat").setTabCompleter(new MuteChatCommand()); + getCommand("staffchat").setExecutor(new StaffChatCommand()); + getCommand("staffchat").setTabCompleter(new StaffChatCommand()); } public void initConfig() { diff --git a/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/MuteChatCommand.java b/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/MuteChatCommand.java new file mode 100644 index 0000000..a966bfe --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/MuteChatCommand.java @@ -0,0 +1,43 @@ +package io.github.itzispyder.ogredupealias.commands.commands; + +import io.github.itzispyder.ogredupealias.commands.CmdExHandler; +import io.github.itzispyder.ogredupealias.commands.TabComplBuilder; +import io.github.itzispyder.ogredupealias.utils.ChatConstraints; +import io.github.itzispyder.ogredupealias.utils.Text; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; + +import java.util.List; + +public class MuteChatCommand implements TabExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + try { + if (args.length == 0) ChatConstraints.setChatMuted(!ChatConstraints.isChatMuted()); + else ChatConstraints.setChatMuted(Boolean.parseBoolean(args[0])); + + boolean muted = ChatConstraints.isChatMuted(); + sender.sendMessage(Text.builder("&3Chat is now " + (muted ? "&cmuted" : "&aunmuted") + "&3!") + .prefix() + .color() + .build()); + } + catch (Exception ex) { + CmdExHandler handler = new CmdExHandler(ex,command); + sender.sendMessage(handler.getHelp()); + } + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return new TabComplBuilder(sender, command, alias, args) + .add(1, new String[] { + "true", + "false" + }) + .build(); + } +} diff --git a/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/StaffChatCommand.java b/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/StaffChatCommand.java new file mode 100644 index 0000000..42df924 --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/commands/commands/StaffChatCommand.java @@ -0,0 +1,41 @@ +package io.github.itzispyder.ogredupealias.commands.commands; + +import io.github.itzispyder.ogredupealias.commands.CmdExHandler; +import io.github.itzispyder.ogredupealias.commands.TabComplBuilder; +import io.github.itzispyder.ogredupealias.utils.ArrayUtils; +import io.github.itzispyder.ogredupealias.utils.ServerUtils; +import io.github.itzispyder.ogredupealias.utils.Text; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; + +import java.util.List; + +public class StaffChatCommand implements TabExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + try { + String msg = String.join(" ", args); + ServerUtils.dmEachPlayer(p -> p.hasPermission("oda.commands.staffchat"), Text.builder() + .message("&7[&bStaffChat&7] &8>> &e" + msg) + .color() + .prefix() + .build()); + } + catch (Exception ex) { + CmdExHandler handler = new CmdExHandler(ex,command); + sender.sendMessage(handler.getHelp()); + } + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return new TabComplBuilder(sender, command, alias, args) + .add(1, ArrayUtils.toNewList(Bukkit.getOnlinePlayers(), Player::getName)) + .build(); + } +} diff --git a/src/main/java/io/github/itzispyder/ogredupealias/data/Config.java b/src/main/java/io/github/itzispyder/ogredupealias/data/Config.java index 57a9448..c213954 100644 --- a/src/main/java/io/github/itzispyder/ogredupealias/data/Config.java +++ b/src/main/java/io/github/itzispyder/ogredupealias/data/Config.java @@ -1,6 +1,8 @@ package io.github.itzispyder.ogredupealias.data; +import io.github.itzispyder.ogredupealias.OgreDupeAlias; import io.github.itzispyder.ogredupealias.utils.Text; +import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -90,6 +92,9 @@ public abstract class Config { public static class Player { private static final String path = "player."; + public static String firstJoinMessage() { + return Text.color(get().getString(path + "first-join-message","&e%player% has joined the game for their first time!")); + } public static String joinMessage() { return Text.color(get().getString(path + "join-message","&e%player% has joined the game")); } diff --git a/src/main/java/io/github/itzispyder/ogredupealias/events/ChatEventListener.java b/src/main/java/io/github/itzispyder/ogredupealias/events/ChatEventListener.java new file mode 100644 index 0000000..52761a2 --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/events/ChatEventListener.java @@ -0,0 +1,19 @@ +package io.github.itzispyder.ogredupealias.events; + +import io.github.itzispyder.ogredupealias.utils.ChatConstraints; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +public class ChatEventListener implements Listener { + + @EventHandler + public void onChat(AsyncPlayerChatEvent e) { + final Player p = e.getPlayer(); + final String msg = e.getMessage(); + final ChatConstraints cc = new ChatConstraints(p,msg); + + if (!cc.passAllChecks()) e.setCancelled(true); + } +} diff --git a/src/main/java/io/github/itzispyder/ogredupealias/events/PlayerEventListener.java b/src/main/java/io/github/itzispyder/ogredupealias/events/PlayerEventListener.java new file mode 100644 index 0000000..8c65984 --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/events/PlayerEventListener.java @@ -0,0 +1,26 @@ +package io.github.itzispyder.ogredupealias.events; + +import io.github.itzispyder.ogredupealias.data.Config; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerEventListener implements Listener { + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + final Player p = e.getPlayer(); + + e.setJoinMessage((p.hasPlayedBefore() ? Config.Player.joinMessage() : Config.Player.firstJoinMessage()).replaceAll("%player%",p.getDisplayName())); + Config.Player.onJoin().forEach(p::chat); + } + + @EventHandler + public void onLeave(PlayerQuitEvent e) { + final Player p = e.getPlayer(); + + e.setQuitMessage(Config.Player.quitMessage().replaceAll("%player%",p.getDisplayName())); + } +} diff --git a/src/main/java/io/github/itzispyder/ogredupealias/utils/ArrayUtils.java b/src/main/java/io/github/itzispyder/ogredupealias/utils/ArrayUtils.java index daa70dc..61669f6 100644 --- a/src/main/java/io/github/itzispyder/ogredupealias/utils/ArrayUtils.java +++ b/src/main/java/io/github/itzispyder/ogredupealias/utils/ArrayUtils.java @@ -1,6 +1,7 @@ package io.github.itzispyder.ogredupealias.utils; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -19,4 +20,14 @@ public abstract class ArrayUtils { e.forEach(i -> list.add(a.apply(i))); return list; } + + public static String list2string(List list) { + return Text.color("&7[&e" + String.join("&7, &e", ArrayUtils.toNewList(list, Object::toString)) + "&7]"); + } + + public static List bind(Iterable tList, T... ts) { + List list = Arrays.asList(ts); + tList.forEach(list::add); + return list; + } } diff --git a/src/main/java/io/github/itzispyder/ogredupealias/utils/ChatConstraints.java b/src/main/java/io/github/itzispyder/ogredupealias/utils/ChatConstraints.java new file mode 100644 index 0000000..c76a3cd --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/utils/ChatConstraints.java @@ -0,0 +1,168 @@ +package io.github.itzispyder.ogredupealias.utils; + +import io.github.itzispyder.ogredupealias.data.Config; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.entity.Player; + +import java.util.*; + +public class ChatConstraints { + + private static final Map chatCooldown = new HashMap<>(); + private static final Map lastMessage = new HashMap<>(); + private static boolean isChatMuted = false; + private final Player player; + private final String message; + + public ChatConstraints(Player player, String message) { + this.player = player; + this.message = this.removeColors(message); + } + + public boolean passAllChecks() { + return passChatMuted() && passRepeat() && passSwear() && passUnicode() && passSpam(); + } + + public boolean passChatMuted() { + if (!isChatMuted) return true; + if (player.hasPermission("oda.chat.bypass")) return true; + player.sendMessage(Text.builder() + .message("&cChat is currently muted! Please contact an administrator if you believe this is a mistake!") + .prefix() + .color() + .build()); + return false; + } + + public boolean passRepeat() { + if (!Config.Chat.AntiRepeat.enabled()) return true; + if (player.hasPermission("oda.chat.bypass.repeat")) return true; + + if (lastMessage.containsKey(player.getName()) && isSimilar(message, lastMessage.get(player.getName()))) { + player.sendMessage(Text.builder("&cPlease do not repeat similar messages!") + .prefix() + .color() + .build()); + return false; + } + lastMessage.put(player.getName(), message); + return true; + } + + public boolean passSpam() { + if (!Config.Chat.AntiSpam.enabled()) return true; + if (player.hasPermission("oda.chat.bypass.spam")) return true; + + if (chatCooldown.containsKey(player.getName()) && chatCooldown.get(player.getName()) > System.currentTimeMillis()) { + player.sendMessage(Text.builder("&cYou can chat again in &e" + getChatCooldown(player) + "&c seconds!") + .prefix() + .color() + .build()); + return false; + } + int cooldownMillis = (int) (Config.Chat.AntiSpam.cooldown() * 1000); + chatCooldown.put(player.getName(), System.currentTimeMillis() + cooldownMillis); + return true; + } + + public boolean passUnicode() { + if (!Config.Chat.AntiUnicode.enabled()) return true; + if (player.hasPermission("oda.chat.bypass.unicode")) return true; + + String nonAllowed = message.replaceAll("[A-Za-z0-9\\[,./?><|\\]()*&^%$#@!~`{}:;'\"-_]","").trim(); + if (nonAllowed.length() == 0) return true; + + TextComponent text = new TextComponent(); + text.setText(Text.builder( + "&cPlease do not send unsupported characters in chat!" + + "\n&cMessage: &f" + message + + "\n&cCaught: &e" + nonAllowed + ).prefix() + .color() + .build()); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(Text.color( + "&cMessage: &f" + message + + "\n&cCaught: &e" + nonAllowed + )))); + player.spigot().sendMessage(text); + ServerUtils.forEachStaff(p -> { + p.spigot().sendMessage(text); + }); + return false; + } + + public boolean passSwear() { + if (!Config.Chat.AntiSwear.enabled()) return true; + if (player.hasPermission("oda.chat.bypass.swear")) return true; + + String s = message.toLowerCase().replaceAll("[^a-z0-9]",""); + String d = s; + List caught = new ArrayList<>(); + + for (String whitelisted : Config.Chat.AntiSwear.whitelist()) { + s = s.replaceAll(whitelisted.toLowerCase(),""); + } + + for (String blacklisted : Config.Chat.AntiSwear.blacklist()) { + if (s.contains(blacklisted.toLowerCase())) { + caught.add(blacklisted); + d = d.replaceAll(blacklisted, Text.color("&e" + blacklisted + "&f")); + } + } + + if (caught.isEmpty()) return true; + + String warn = ArrayUtils.list2string(caught); + TextComponent text = new TextComponent(); + text.setText(Text.builder( + "&cPlease do not swear in chat!" + + "\n&cMessage: &f" + d + + "\n&cCaught: &e" + warn + ).prefix() + .color() + .build()); + text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(Text.color( + "&cMessage: &f" + d + + "\n&cCaught: &e" + warn + )))); + player.spigot().sendMessage(text); + ServerUtils.forEachStaff(p -> { + p.spigot().sendMessage(text); + }); + return false; + } + + private String removeColors(String msg) { + return String.join(" ", Arrays.stream(msg.split(" ")) + .filter(s -> !(s.length() >= 1 && s.charAt(0) == 'ยง')) + .toList()); + } + + public double getChatCooldown() { + return getChatCooldown(player); + } + + public static double getChatCooldown(Player p) { + if (!chatCooldown.containsKey(p.getName())) return 0.0; + if (chatCooldown.get(p.getName()) <= System.currentTimeMillis()) return 0.0; + + return Math.floor((chatCooldown.get(p.getName()) - System.currentTimeMillis()) / 10.0) / 100.0; + } + + private boolean isSimilar(String s1, String s2) { + float max = Math.max(s1.length(), s2.length()); + float min = Math.min(s1.length(), s2.length()); + double lengthRatio = min / max; + if (lengthRatio < 0.6) return false; + return s1.toLowerCase().contains(s2.toLowerCase()) || s2.toLowerCase().contains(s1.toLowerCase()); + } + + public static void setChatMuted(boolean muted) { + isChatMuted = muted; + } + + public static boolean isChatMuted() { + return isChatMuted; + } +} diff --git a/src/main/java/io/github/itzispyder/ogredupealias/utils/ServerUtils.java b/src/main/java/io/github/itzispyder/ogredupealias/utils/ServerUtils.java new file mode 100644 index 0000000..d834a13 --- /dev/null +++ b/src/main/java/io/github/itzispyder/ogredupealias/utils/ServerUtils.java @@ -0,0 +1,47 @@ +package io.github.itzispyder.ogredupealias.utils; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class ServerUtils { + + public static List getPlayers() { + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } + + public static List getStaff() { + return getPlayers().stream().filter(Player::isOp).toList(); + } + + public static void forEachPlayer(Consumer consumer) { + getPlayers().forEach(consumer); + } + + public static void forEachStaff(Consumer consumer) { + getStaff().forEach(consumer); + } + + public static void dmEachPlayer(Predicate condition, String dm) { + forEachPlayer(p -> { + if (condition.test(p)) p.sendMessage(dm); + }); + } + + public static void dmEachPlayer(String dm) { + forEachPlayer(p -> p.sendMessage(dm)); + } + + public static void forEachSpecified(Iterable players, Consumer consumer) { + players.forEach(consumer); + } + + public static void forEachSpecified(Consumer consumer, Player... players) { + Arrays.stream(players).forEach(consumer); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index a8c184b..2b6a04c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -5,9 +5,9 @@ chat: anti-swear: enabled: true whitelist: - - d + - glass blacklist: - - d + - ass anti-spam: enabled: true cooldown: 5 @@ -17,6 +17,7 @@ chat: enabled: true player: + first-join-message: "%player% has joined the game for their first time!" join-message: "&e%player% has joined the game" quit-message: "&e%player% has left the game" on-join: diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6407aa3..be623a2 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -6,7 +6,53 @@ prefix: ODA authors: [ ImproperIssues, TheTrouper ] description: Server utilities for OgreDupe.minehut.gg website: https://itzispyder.github.io/ + +permissions: + oda.commands.config: + description: Able to manage config from in game + default: op + oda.commands.mutechat: + description: Able to mute chat + default: op + oda.commands.staffchat: + description: Access to staffchat. + default: op + oda.chat.bypass: + description: Bypass chat restrictions + default: op + children: + - oda.chat.bypass.swear + - oda.chat.bypass.spam + - oda.chat.bypass.repeat + - oda.chat.bypass.unicode + oda.chat.bypass.swear: + description: Bypass anti-swear + default: op + oda.chat.bypass.spam: + description: Bypass anti-spam + default: op + oda.chat.bypass.repeat: + description: Bypass anti-repeat + default: op + oda.chat.bypass.unicode: + description: Bypass anti-unicode + default: op + commands: config: description: Config management - usage: /config [get|set] \ No newline at end of file + usage: /config [get|set] + permission: oda.commands.config + aliases: + - configuration + mutechat: + description: Mutes the chat + usage: /mutechat [true|false] + permission: oda.commands.mutechat + staffchat: + description: Staff chat + usage: /staffchat [] + permission: oda.commands.staffchat + aliases: + - sc + - schat \ No newline at end of file