diff --git a/src/main/java/de/viper/globalchat/GlobalChat.java b/src/main/java/de/viper/globalchat/GlobalChat.java deleted file mode 100644 index 43a79b2..0000000 --- a/src/main/java/de/viper/globalchat/GlobalChat.java +++ /dev/null @@ -1,575 +0,0 @@ -package de.viper.globalchat; - -import net.luckperms.api.LuckPerms; -import net.luckperms.api.LuckPermsProvider; -import net.luckperms.api.model.user.User; -import net.luckperms.api.cacheddata.CachedMetaData; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.chat.HoverEvent.Action; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.ChatEvent; -import net.md_5.bungee.api.event.ServerConnectEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.api.plugin.Command; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.event.EventHandler; - -import java.io.*; -import java.nio.file.*; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; - -/** - * GlobalChat - Bungee Plugin - * - Filter (filter.yml) - * - Logs (plugins/GlobalChat/logs/YYYY-MM-DD.log) - * - /support, /reply, /info - * - Prefix/Suffix via LuckPerms (Proxy-side) - * - ServerSwitch announcement (sends message to chat when a player switches from one server to another) - * - Attempts to suppress Spigot join/quit messages during server switch - */ -public class GlobalChat extends Plugin implements Listener { - - private static final String CHANNEL_CONTROL = "global:control"; - - private List badWords = new ArrayList<>(); - private File logFolder; - private boolean chatMuted = false; - - // Optional: falls Spigot-Server OP-Info per PluginMessage sendet - private final Map playerIsOp = new ConcurrentHashMap<>(); - // Letzte Support-Kontakte (staff UUID -> target UUID) - private final Map lastSupportContact = new ConcurrentHashMap<>(); - // Tracking für unterdrückte Join-/Quit-Nachrichten - private final Set suppressJoinQuit = ConcurrentHashMap.newKeySet(); - - // =========================== - // Neu: Welcome-Nachrichten - // =========================== - private List welcomeMessages = new ArrayList<>(); - - private void loadWelcomeMessages() { - File file = new File(getDataFolder(), "welcome.yml"); - if (!file.exists()) { - try { - getDataFolder().mkdirs(); - file.createNewFile(); - try (PrintWriter out = new PrintWriter(file)) { - out.println("welcome-messages:"); - out.println(" - \"&aWillkommen, %player%! Viel Spaß auf unserem Server!\""); - out.println(" - \"&aHey %player%, schön dich hier zu sehen! Los geht's!\""); - out.println(" - \"&a%player%, dein Abenteuer beginnt jetzt! Viel Spaß!\""); - out.println(" - \"&aWillkommen an Bord, %player%! Entdecke den Server!\""); - out.println(" - \"&a%player%, herzlich willkommen! Lass uns loslegen!\""); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - try { - List lines = Files.readAllLines(file.toPath()); - welcomeMessages.clear(); - for (String line : lines) { - line = line.trim(); - if (line.startsWith("-")) welcomeMessages.add(line.substring(1).trim()); - } - getLogger().info("§eGeladene Welcome-Nachrichten: " + welcomeMessages.size()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void sendRandomWelcomeMessage(ProxiedPlayer player) { - if (welcomeMessages.isEmpty()) return; - - Random rand = new Random(); - String message = welcomeMessages.get(rand.nextInt(welcomeMessages.size())); - message = message.replace("%player%", player.getName()); - message = ChatColor.translateAlternateColorCodes('&', message); - - player.sendMessage(new TextComponent(message)); - } - - @Override - public void onEnable() { - // Plugin channel registrieren (für Steuer-Nachrichten an Spigot) - try { - getProxy().registerChannel(CHANNEL_CONTROL); - } catch (Throwable ignored) { - getLogger().warning("Konnte Kanal " + CHANNEL_CONTROL + " nicht registrieren."); - } - - getProxy().getPluginManager().registerListener(this, this); - - loadFilter(); - loadWelcomeMessages(); - - logFolder = new File(getDataFolder(), "logs"); - if (!logFolder.exists()) logFolder.mkdirs(); - cleanupOldLogs(); - - // Befehle registrieren - getProxy().getPluginManager().registerCommand(this, new ReloadCommand()); - getProxy().getPluginManager().registerCommand(this, new MuteCommand()); - getProxy().getPluginManager().registerCommand(this, new SupportCommand()); - getProxy().getPluginManager().registerCommand(this, new ReplyCommand()); - getProxy().getPluginManager().registerCommand(this, new InfoCommand()); - - getLogger().info("§aGlobalChat aktiviert (Zensur, Logs, Reload, Mute, Support, Reply & Info)!"); - } - - @Override - public void onDisable() { - getLogger().info("§cGlobalChat deaktiviert!"); - try { - getProxy().unregisterChannel(CHANNEL_CONTROL); - } catch (Throwable ignored) { - getLogger().warning("Konnte Kanal " + CHANNEL_CONTROL + " nicht deregistrieren."); - } - } - - // =========================== - // Chatfilter & Global-Chat - // =========================== - @EventHandler - public void onChat(ChatEvent e) { - if (!(e.getSender() instanceof ProxiedPlayer)) return; - if (e.isCommand()) return; - - ProxiedPlayer player = (ProxiedPlayer) e.getSender(); - String originalMsg = e.getMessage(); - - // Debugging: Logge alle Chat-Nachrichten, um zu prüfen, ob Join-/Quit-Nachrichten ankommen - getLogger().info("ChatEvent: Spieler=" + player.getName() + ", Nachricht=" + originalMsg); - - // Versuche, Join-/Quit-Nachrichten zu filtern - if (suppressJoinQuit.contains(player.getUniqueId()) && - (originalMsg.contains("joined the Game") || originalMsg.contains("left the Game"))) { - getLogger().info("Unterdrücke Join-/Quit-Nachricht für " + player.getName() + ": " + originalMsg); - e.setCancelled(true); - return; - } - - // Globaler Mute - if (chatMuted && !player.hasPermission("globalchat.bypass")) { - player.sendMessage(new TextComponent("§cDer globale Chat ist derzeit deaktiviert!")); - e.setCancelled(true); - return; - } - - // Badword-Zensur (nur für Chat-Ausgabe; Log bleibt unzensiert) - String censoredMsg = originalMsg; - for (String bad : badWords) { - if (bad == null || bad.trim().isEmpty()) continue; - censoredMsg = censoredMsg.replaceAll("(?i)" + Pattern.quote(bad), repeat("*", bad.length())); - } - - e.setCancelled(true); - - String serverName = player.getServer().getInfo().getName(); - - // Prefix/Suffix holen (LuckPerms Proxy-side) - String[] ps = getPrefixSuffix(player); - String prefix = ps[0] == null ? "" : ps[0].trim(); - String suffix = ps[1] == null ? "" : ps[1].trim(); - - // Entscheide: zeige entweder Prefix (falls vorhanden) oder, falls kein Prefix, das Suffix. - String displayTag = ""; - if (!prefix.isEmpty()) { - displayTag = prefix; - } else if (!suffix.isEmpty()) { - displayTag = suffix; - } - - // saubere Leerzeichen um displayTag - if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " "; - - // Format: [Server] Name: Nachricht - StringBuilder out = new StringBuilder(); - out.append("§7[").append(serverName).append("] "); - if (!displayTag.isEmpty()) out.append(displayTag); - out.append(player.getName()); - out.append("§f: ").append(censoredMsg); - - String chatOut = out.toString(); - - for (ProxiedPlayer p : getProxy().getPlayers()) { - p.sendMessage(new TextComponent(chatOut)); - } - - // Log unzensiert (ohne color codes) - String logEntry = "[" + serverName + "] " + - (displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") + - player.getName() + - ": " + originalMsg; - logMessage(logEntry); - } - - // =========================== - // Server connect (pre-switch suppression) - // =========================== - @EventHandler - public void onServerConnect(ServerConnectEvent e) { - if (e.isCancelled()) return; - - ProxiedPlayer player = e.getPlayer(); - ServerInfo target = e.getTarget(); - ServerInfo from = player.getServer() != null ? player.getServer().getInfo() : null; - - if (from == null || from.equals(target)) return; - - // Markiere Spieler für Unterdrückung - suppressJoinQuit.add(player.getUniqueId()); - getLogger().info("Markiert " + player.getName() + " für Join-/Quit-Unterdrückung"); - - // Suppress quit on old server - try { - sendSuppressJoinQuit(from, player.getUniqueId()); - getLogger().info("Sent suppress quit message for " + player.getName() + " to server " + from.getName()); - } catch (Throwable ex) { - getLogger().warning("Fehler beim Senden der Quit-Unterdrückung an " + from.getName() + ": " + ex.getMessage()); - } - - // Suppress join on new server - try { - sendSuppressJoinQuit(target, player.getUniqueId()); - getLogger().info("Sent suppress join message for " + player.getName() + " to server " + target.getName()); - } catch (Throwable ex) { - getLogger().warning("Fehler beim Senden der Join-Unterdrückung an " + target.getName() + ": " + ex.getMessage()); - } - - // Entferne Unterdrückung nach kurzer Zeit (2 Sekunden) - getProxy().getScheduler().schedule(this, () -> { - suppressJoinQuit.remove(player.getUniqueId()); - getLogger().info("Entfernte Unterdrückung für " + player.getName()); - }, 2, java.util.concurrent.TimeUnit.SECONDS); - } - - // =========================== - // Server switch announcement - // =========================== - @EventHandler - public void onServerSwitch(ServerSwitchEvent e) { - ProxiedPlayer player = e.getPlayer(); - ServerInfo from = e.getFrom(); // kann null sein beim ersten Join - ServerInfo to = player.getServer() != null ? player.getServer().getInfo() : null; - - if (to == null) return; - - // Wenn from == null -> erster Join, keine Nachricht senden - if (from == null) return; - - // Nur senden, wenn Server wirklich gewechselt wurde - if (from.getName().equalsIgnoreCase(to.getName())) return; - - String fromName = from.getName(); - String toName = to.getName(); - - // Prefix/Suffix für Anzeige (show either prefix or suffix) - String[] ps = getPrefixSuffix(player); - String prefix = ps[0] == null ? "" : ps[0].trim(); - String suffix = ps[1] == null ? "" : ps[1].trim(); - - String displayTag = ""; - if (!prefix.isEmpty()) displayTag = prefix; - else if (!suffix.isEmpty()) displayTag = suffix; - - if (!displayTag.isEmpty() && !displayTag.endsWith(" ")) displayTag = displayTag + " "; - - StringBuilder msg = new StringBuilder(); - msg.append("§7[").append(toName).append("] "); - if (!displayTag.isEmpty()) msg.append(displayTag); - msg.append(player.getName()); - msg.append(" §7hat den Server gewechselt: §e") - .append(fromName).append(" §7→ §e").append(toName).append("§7."); - - String finalMsg = msg.toString(); - - // An alle Spieler senden (sichtbar im Chat) - for (ProxiedPlayer p : getProxy().getPlayers()) { - p.sendMessage(new TextComponent(finalMsg)); - } - - // Log (ohne Farb-Codes) - String logEntry = "[" + toName + "] " + - (displayTag.isEmpty() ? "" : stripColor(displayTag) + " ") + - player.getName() + " hat den Server gewechselt: " + fromName + " -> " + toName + "."; - logMessage(logEntry); - } - - /** - * Sendet an den Ziel-Server eine PluginMessage mit dem Befehl, Join/Quit für den Spieler zu unterdrücken. - * Format: writeUTF("suppress"); writeUTF(playerUUID) - */ - private void sendSuppressJoinQuit(ServerInfo server, UUID playerId) { - if (server == null) return; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream(baos)) { - out.writeUTF("suppress"); - out.writeUTF(playerId.toString()); - server.sendData(CHANNEL_CONTROL, baos.toByteArray()); - } catch (IOException ex) { - getLogger().warning("Fehler beim Senden der suppress-Nachricht an " + server.getName() + ": " + ex.getMessage()); - } - } - - // Java8-kompatible repeat - private String repeat(String str, int count) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < count; i++) sb.append(str); - return sb.toString(); - } - - // =========================== - // Prefix/Suffix via LuckPerms (Proxy-side) - // =========================== - private String[] getPrefixSuffix(ProxiedPlayer player) { - String prefix = ""; - String suffix = ""; - - try { - LuckPerms lp = LuckPermsProvider.get(); - if (lp != null) { - // Versuche schnell aus dem Cache zu holen - User user = lp.getUserManager().getUser(player.getUniqueId()); - - // Falls nicht im Cache: synchrones Laden (Fallback) — kann blockieren, aber stellt Prefix sicher - if (user == null) { - try { - user = lp.getUserManager().loadUser(player.getUniqueId()).join(); - } catch (Exception ignored) { - user = null; - } - } - - if (user != null) { - CachedMetaData meta = user.getCachedData().getMetaData(); - if (meta != null) { - String p = meta.getPrefix(); - String s = meta.getSuffix(); - if (p != null) prefix = p; - if (s != null) suffix = s; - } - } - } - } catch (Throwable ignored) { - // LuckPerms nicht vorhanden oder andere Fehler -> kein Prefix/Suffix - } - - // Farbcodes übersetzen (& -> §) - if (prefix != null && !prefix.isEmpty()) prefix = ChatColor.translateAlternateColorCodes('&', prefix); - if (suffix != null && !suffix.isEmpty()) suffix = ChatColor.translateAlternateColorCodes('&', suffix); - - if (prefix == null) prefix = ""; - if (suffix == null) suffix = ""; - - return new String[]{prefix, suffix}; - } - - // Entfernt Bungee-Farbcodes aus Strings (für saubere Logs) - private String stripColor(String s) { - if (s == null) return ""; - return ChatColor.stripColor(s); - } - - // =========================== - // Filter einlesen - // =========================== - private void loadFilter() { - File file = new File(getDataFolder(), "filter.yml"); - if (!file.exists()) { - try { - getDataFolder().mkdirs(); - file.createNewFile(); - try (PrintWriter out = new PrintWriter(file)) { - out.println("badwords:"); - out.println(" - arsch"); - out.println(" - hurensohn"); - out.println(" - scheiße"); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - try { - List lines = Files.readAllLines(file.toPath()); - badWords.clear(); - for (String line : lines) { - line = line.trim(); - if (line.startsWith("-")) badWords.add(line.substring(1).trim()); - } - getLogger().info("§eGeladene Filter-Wörter: " + badWords.size()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - // =========================== - // Logs aufräumen / schreiben - // =========================== - private void cleanupOldLogs() { - File[] files = logFolder.listFiles(); - if (files == null) return; - - long now = System.currentTimeMillis(); - long sevenDays = 1000L * 60 * 60 * 24 * 7; - - for (File f : files) { - if (now - f.lastModified() > sevenDays) { - f.delete(); - } - } - } - - private void logMessage(String message) { - String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); - File logFile = new File(logFolder, date + ".log"); - - try (BufferedWriter bw = new BufferedWriter(new FileWriter(logFile, true))) { - String time = new SimpleDateFormat("HH:mm:ss").format(new Date()); - bw.write("[" + time + "] " + message); - bw.newLine(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - // =========================== - // Staff-Check (Op / team perms) - // =========================== - private boolean isStaff(ProxiedPlayer p) { - if (p == null) return false; - Boolean reportedOp = playerIsOp.get(p.getUniqueId()); - if (reportedOp != null && reportedOp) return true; - - if (p.hasPermission("team")) return true; - if (p.hasPermission("bungeecord.admin")) return true; - if (p.hasPermission("globalchat.op")) return true; - if (p.hasPermission("*")) return true; - - if (p.hasPermission("bungeecord.command.alert")) return true; - if (p.hasPermission("bungeecord.command.reload")) return true; - if (p.hasPermission("bungeecord.command.kick")) return true; - if (p.hasPermission("bungeecord.command.send")) return true; - if (p.hasPermission("bungeecord.command.perms")) return true; - - return false; - } - - // =========================== - // Commands - // =========================== - public class ReloadCommand extends Command { - public ReloadCommand() { super("globalreload", "globalchat.reload"); } - @Override - public void execute(CommandSender sender, String[] args) { - loadFilter(); - sender.sendMessage(new TextComponent("§aFilter wurde neu geladen!")); - } - } - - public class MuteCommand extends Command { - public MuteCommand() { super("globalmute", "globalchat.mute"); } - @Override - public void execute(CommandSender sender, String[] args) { - chatMuted = !chatMuted; - String status = chatMuted ? "§caktiviert" : "§aaufgehoben"; - for (ProxiedPlayer p : getProxy().getPlayers()) { - p.sendMessage(new TextComponent("§7[GlobalChat] §eDer globale Chat Mute wurde " + status + "§e!")); - } - getLogger().info("GlobalMute wurde " + (chatMuted ? "aktiviert" : "deaktiviert") + "."); - } - } - - // /support - public class SupportCommand extends Command { - public SupportCommand() { super("support"); } - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { - sender.sendMessage(new TextComponent("§cNur Spieler können Support-Nachrichten senden.")); - return; - } - - ProxiedPlayer player = (ProxiedPlayer) sender; - if (args.length == 0) { - player.sendMessage(new TextComponent("§cBitte eine Nachricht angeben: /support ")); - return; - } - - String msg = String.join(" ", args); - String serverName = player.getServer().getInfo().getName(); - - TextComponent supportMsg = new TextComponent("§7[Support] §b" + player.getName() + " §7vom Server §e" + serverName + " §7: §f" + msg); - supportMsg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new ComponentBuilder("Klicke, um /reply " + player.getName() + " zu schreiben").create())); - supportMsg.setClickEvent(new net.md_5.bungee.api.chat.ClickEvent(net.md_5.bungee.api.chat.ClickEvent.Action.SUGGEST_COMMAND, "/reply " + player.getName() + " ")); - - // an alle Staff - for (ProxiedPlayer p : getProxy().getPlayers()) { - if (isStaff(p)) { - p.sendMessage(supportMsg); - lastSupportContact.put(p.getUniqueId(), player.getUniqueId()); - } - } - - player.sendMessage(new TextComponent("§aDeine Support-Nachricht wurde gesendet.")); - logMessage("[Support][" + serverName + "] " + player.getName() + ": " + msg); - } - } - - // /reply - public class ReplyCommand extends Command { - public ReplyCommand() { super("reply"); } - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) return; - - ProxiedPlayer staff = (ProxiedPlayer) sender; - UUID targetId = lastSupportContact.get(staff.getUniqueId()); - if (targetId == null) { - staff.sendMessage(new TextComponent("§cKein Spieler zum Antworten gefunden.")); - return; - } - - if (args.length == 0) { - staff.sendMessage(new TextComponent("§cBitte eine Nachricht angeben.")); - return; - } - - ProxiedPlayer target = getProxy().getPlayer(targetId); - if (target == null) { - staff.sendMessage(new TextComponent("§cSpieler ist nicht online.")); - return; - } - - String msg = String.join(" ", args); - target.sendMessage(new TextComponent("§7[Reply von §b" + staff.getName() + "§7]: §f" + msg)); - staff.sendMessage(new TextComponent("§aDeine Nachricht wurde an §b" + target.getName() + "§a gesendet.")); - } - } - - // /info - public class InfoCommand extends Command { - public InfoCommand() { super("info"); } - @Override - public void execute(CommandSender sender, String[] args) { - sender.sendMessage(new TextComponent("§8§m------------------------------")); - sender.sendMessage(new TextComponent("§6§lGlobalChat Info")); - sender.sendMessage(new TextComponent("§ePlugin-Name: §b" + getDescription().getName())); - sender.sendMessage(new TextComponent("§eVersion: §b" + getDescription().getVersion())); - sender.sendMessage(new TextComponent("§eErsteller: §bM_Viper")); - sender.sendMessage(new TextComponent("§8§m------------------------------")); - } - } -} \ No newline at end of file