diff --git a/src/main/java/net/viper/status/modules/chat/ChatModule.java b/src/main/java/net/viper/status/modules/chat/ChatModule.java deleted file mode 100644 index 05711b6..0000000 --- a/src/main/java/net/viper/status/modules/chat/ChatModule.java +++ /dev/null @@ -1,1400 +0,0 @@ -package net.viper.status.modules.chat; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.*; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.ChatEvent; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -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 net.md_5.bungee.event.EventPriority; -import net.viper.status.module.Module; -import net.viper.status.modules.chat.bridge.DiscordBridge; -import net.viper.status.modules.chat.bridge.TelegramBridge; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -/** - * ChatModule für StatusAPI (BungeeCord) - * - * Features: - * ✅ Mehrere Kanäle mit Permissions - * ✅ Server-Erkennung im Chat-Format - * ✅ /helpop für Spieler - * ✅ Emoji-Unterstützung (:smile: → 😊) - * ✅ Admin-Mute / Spieler-eigener Chat-Mute - * ✅ CMI & Plugin-kompatibel (kein Eingriff in SubServer-Befehle) - * ✅ Privat-Nachrichten (/msg, /r) - * ✅ Spieler-Blocking (/ignore, /unignore) - * ✅ Discord & Telegram Integration - * ✅ Admin-Bypass (kann nicht gemutet/geblockt werden) - * ✅ /broadcast für Admins - * ✅ Secure-Chat kompatibel (1.19+ & Bedrock/Geyser) - * ✅ Chat-Log mit konfigurierbarer Aufbewahrung (7 / 14 Tage) - * ✅ Nachrichten-IDs (klickbar → Zwischenablage) - * ✅ Report-System (/report, /reports, /reportclose) - * ✅ Admin-Benachrichtigung bei Reports (sofort oder verzögert nach Login) - * ✅ Server-Farben pro Server (&-Codes und &#RRGGBB HEX) - * ✅ Join / Leave Nachrichten (mit Vanish-Support) - * ✅ BungeeCord-Vanish-Integration via VanishProvider - */ -public class ChatModule implements Module, Listener { - - private Plugin plugin; - private Logger logger; - - private ChatConfig config; - private MuteManager muteManager; - private BlockManager blockManager; - private PrivateMsgManager pmManager; - private EmojiParser emojiParser; - private ChatFilter chatFilter; - private DiscordBridge discordBridge; - private TelegramBridge telegramBridge; - private ChatLogger chatLogger; - private ReportManager reportManager; - private AccountLinkManager linkManager; - - // UUID → aktiver Kanal-ID - private final Map playerChannels = new ConcurrentHashMap<>(); - - // UUIDs die ihren eigenen Chat-Empfang deaktiviert haben - private final Set selfChatMuted = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - // UUIDs die Mentions für sich deaktiviert haben - private final Set mentionsDisabled = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - // HelpOp Cooldown: UUID → letzter Zeitstempel (Sekunden) - private final Map helpopCooldowns = new ConcurrentHashMap<>(); - - // Report-Cooldown: UUID → letzter Report-Zeitstempel (Sekunden) - private final Map reportCooldowns = new ConcurrentHashMap<>(); - - // Letzte Chatnachricht pro Spieler (für Report-Kontext): name.toLowerCase() → message - private final Map lastChatMessages = new ConcurrentHashMap<>(); - - // UUIDs die gerade auf Plugin-Chat-Eingabe warten - private final Set awaitingInput = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - // Geyser-Präfix für Bedrock-Spieler (Standard: ".") - private static final String GEYSER_PREFIX = "."; - - @Override - public String getName() { return "ChatModule"; } - - @Override - public void onEnable(Plugin plugin) { - this.plugin = plugin; - this.logger = plugin.getLogger(); - - // Konfiguration laden - config = new ChatConfig(plugin); - config.load(); - - // Manager initialisieren - muteManager = new MuteManager(plugin.getDataFolder(), logger); - muteManager.load(); - - blockManager = new BlockManager(plugin.getDataFolder(), logger); - blockManager.load(); - - pmManager = new PrivateMsgManager(blockManager); - emojiParser = new EmojiParser(config.getEmojiMappings(), config.isEmojiEnabled()); - chatFilter = new ChatFilter(config.getFilterConfig()); - - // ChatLogger - if (config.isChatlogEnabled()) { - chatLogger = new ChatLogger(plugin.getDataFolder(), logger, config.getChatlogRetentionDays()); - logger.info("[ChatModule] Chat-Log aktiviert (" + config.getChatlogRetentionDays() + " Tage Aufbewahrung)."); - } - - // ReportManager - if (config.isReportsEnabled()) { - reportManager = new ReportManager(plugin.getDataFolder(), logger); - reportManager.load(); - } - - // AccountLinkManager - linkManager = new AccountLinkManager(plugin.getDataFolder(), logger); - linkManager.load(); - - // Externe Brücken - if (config.isDiscordEnabled() || config.isReportWebhookEnabled()) { - discordBridge = new DiscordBridge(plugin, config); - discordBridge.setLinkManager(linkManager); - if (config.isDiscordEnabled()) { - discordBridge.start(); - } - } - if (config.isTelegramEnabled()) { - telegramBridge = new TelegramBridge(plugin, config); - telegramBridge.setLinkManager(linkManager); - telegramBridge.start(); - } - - // Listener & Befehle registrieren - ProxyServer.getInstance().getPluginManager().registerListener(plugin, this); - registerCommands(); - - logger.info("[ChatModule] Aktiviert – " + config.getChannels().size() + " Kanäle geladen."); - } - - @Override - public void onDisable(Plugin plugin) { - if (discordBridge != null) discordBridge.stop(); - if (telegramBridge != null) telegramBridge.stop(); - if (muteManager != null) muteManager.save(); - if (blockManager != null) blockManager.save(); - if (reportManager != null) reportManager.save(); - if (linkManager != null) linkManager.save(); - playerChannels.clear(); - selfChatMuted.clear(); - helpopCooldowns.clear(); - reportCooldowns.clear(); - lastChatMessages.clear(); - } - - // ========================================================= - // CHAT-EVENTS (BungeeCord original, 1.20+) - // - // Das Bypass-Problem mit Paper: - // Wenn BungeeCord eine signierte Nachricht unverändert durchlässt - // (kein setCancelled), prüft Paper die Signatur → ungültig → Fehler. - // Wenn wir setCancelled(true) setzen und die Nachricht selbst senden, - // fehlt die Signatur → Paper lehnt sie ebenfalls ab. - // - // Lösung: In der paper-global.yml auf JEDEM Sub-Server: - // messages: - // reject-chat-unsigned: false - // Das erlaubt unsignierte Nachrichten vom Proxy durch. - // Alternativ: In spigot.yml → settings: bungeecord: true (bereits nötig) - // kombiniert mit BungeeCord IP-Forwarding deaktiviert Paper die Prüfung. - // ========================================================= - - @EventHandler(priority = EventPriority.HIGHEST) - public void onChat(ChatEvent e) { - if (e.isCancelled()) return; - if (e.isCommand()) return; - if (!(e.getSender() instanceof ProxiedPlayer)) return; - ProxiedPlayer player = (ProxiedPlayer) e.getSender(); - - if (awaitingInput.contains(player.getUniqueId())) { - awaitingInput.remove(player.getUniqueId()); - return; // Event NICHT cancellen → Nachricht geht mit Originalsignatur durch - } - - e.setCancelled(true); - processChat(player, e.getMessage()); - } - - /** - * Zentrale Chat-Verarbeitungslogik. - */ - private void processChat(ProxiedPlayer player, String rawMessage) { - if (rawMessage == null || rawMessage.trim().isEmpty()) return; - UUID uuid = player.getUniqueId(); - - boolean isAdmin = player.hasPermission(config.getAdminBypassPermission()); - - if (!isAdmin && muteManager.isMuted(uuid)) { - String remaining = muteManager.getRemainingTime(uuid); - player.sendMessage(color(config.getMutedMessage().replace("{time}", remaining))); - return; - } - - String channelId = playerChannels.getOrDefault(uuid, config.getDefaultChannelId()); - ChatChannel channel = config.getChannel(channelId); - if (channel == null) channel = config.getDefaultChannel(); - - if (channel.hasPermission() && !player.hasPermission(channel.getPermission())) { - player.sendMessage(color("&cDu hast keine Berechtigung für den Kanal &f" + channel.getName() + "&c.")); - channelId = config.getDefaultChannelId(); - channel = config.getDefaultChannel(); - playerChannels.put(uuid, channelId); - } - - String message = emojiParser.parse(rawMessage); - - // ── Chat-Filter ── - boolean hasColorPerm = player.hasPermission("chat.color"); - boolean hasFormatPerm = player.hasPermission("chat.color.format"); - boolean filterBypass = isAdmin || player.hasPermission("chat.filter.bypass"); - ChatFilter.FilterResponse filterResp = chatFilter.filter( - uuid, message, filterBypass, hasColorPerm, hasFormatPerm); - - if (filterResp.result == ChatFilter.FilterResult.BLOCKED) { - player.sendMessage(color(filterResp.denyReason)); - return; - } - message = filterResp.message; - - String serverName = player.getServer() != null - ? player.getServer().getInfo().getName() - : "Proxy"; - - String prefix = getLuckPermsPrefix(player); - String suffix = getLuckPermsSuffix(player); - - // ── Mentions erkennen (@Spielername) ── - final Set mentionedPlayers = new java.util.HashSet<>(); - final String messageWithMentions; - if (config.isMentionsEnabled()) { - String[] words = message.split(" "); - StringBuilder mentionBuilder = new StringBuilder(); - String highlightColor = config.getMentionsHighlightColor(); - for (int wi = 0; wi < words.length; wi++) { - String word = words[wi]; - if (word.startsWith("@") && word.length() > 1) { - String targetName = word.substring(1); - ProxiedPlayer mentioned = ProxyServer.getInstance().getPlayer(targetName); - if (mentioned != null && !mentioned.getUniqueId().equals(uuid)) { - mentionedPlayers.add(mentioned.getUniqueId()); - word = translateColors(highlightColor + word + "&r"); - } - } - mentionBuilder.append(word); - if (wi < words.length - 1) mentionBuilder.append(" "); - } - messageWithMentions = mentionBuilder.toString(); - } else { - messageWithMentions = message; - } - - final String finalMessage = messageWithMentions; - final String formatted = buildFormat(channel.getFormat(), serverName, prefix, - player.getName(), suffix, finalMessage); - - // Nachricht loggen und ID generieren - final String msgId; - if (chatLogger != null) { - msgId = chatLogger.generateMessageId(); - chatLogger.log(msgId, serverName, channel.getId(), player.getName(), finalMessage); - } else { - msgId = null; - } - - // Letzte Nachricht des Spielers speichern (für Report-Kontext) - lastChatMessages.put(player.getName().toLowerCase(), finalMessage); - - final ChatChannel finalChannel = channel; - final String finalFormatted = formatted; - final String finalSenderName = player.getName(); - - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - for (ProxiedPlayer recipient : ProxyServer.getInstance().getPlayers()) { - boolean isSelf = recipient.getUniqueId().equals(uuid); - - if (!isSelf && selfChatMuted.contains(recipient.getUniqueId())) continue; - - boolean recipientIsAdmin = recipient.hasPermission(config.getAdminBypassPermission()); - if (!recipientIsAdmin && !blockManager.canReceive(recipient.getUniqueId(), uuid)) continue; - - if (finalChannel.isLocalOnly()) { - if (player.getServer() == null || recipient.getServer() == null) continue; - if (!player.getServer().getInfo().getName() - .equals(recipient.getServer().getInfo().getName())) continue; - } - - if (finalChannel.hasPermission() && !recipient.hasPermission(finalChannel.getPermission())) { - if (!recipient.getUniqueId().equals(uuid)) continue; - } - - // Mention-Benachrichtigung - boolean isMentioned = mentionedPlayers.contains(recipient.getUniqueId()) - && config.isMentionsEnabled() - && !mentionsDisabled.contains(recipient.getUniqueId()); - - if (isMentioned) { - recipient.sendMessage(color(config.getMentionsNotifyPrefix() - + "&7" + finalSenderName + " &7hat dich erwähnt!")); - sendMentionSound(recipient, config.getMentionsSound()); - } - - if (msgId != null) { - recipient.sendMessage(buildClickableMessage(msgId, finalFormatted, finalSenderName)); - } else { - recipient.sendMessage(color(finalFormatted)); - } - } - - bridgeToDiscord(finalChannel, player.getName(), finalMessage, serverName); - bridgeToTelegram(finalChannel, player.getName(), finalMessage, serverName); - }); - } - - // ========================================================= - // LOGIN-EVENT: Kanal setzen + Join-Nachricht + Report-Info - // ========================================================= - - @EventHandler - public void onLogin(PostLoginEvent e) { - ProxiedPlayer player = e.getPlayer(); - UUID uuid = player.getUniqueId(); - - // Standard-Kanal setzen - playerChannels.put(uuid, config.getDefaultChannelId()); - - // Join-Nachricht und Report-Benachrichtigung mit kurzem Delay senden, - // damit alle anderen Proxy-Initialisierungen (inkl. VanishModule) abgeschlossen sind. - // 2s statt 1s: VanishModule markiert den Spieler oft erst beim ServerConnectedEvent. - plugin.getProxy().getScheduler().schedule(plugin, () -> { - if (!player.isConnected()) return; - - // ── Join-Nachricht ── - if (config.isJoinLeaveEnabled()) { - broadcastJoinLeave(player, true); - } - - // ── Offene Reports für Admins anzeigen ── - if (reportManager != null - && (player.hasPermission(config.getAdminNotifyPermission()) - || player.hasPermission(config.getAdminBypassPermission()))) { - int count = reportManager.getOpenCount(); - if (count > 0) { - player.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - player.sendMessage(color("&c&l⚑ OFFENE REPORTS &8| &f" + count + " ausstehend")); - player.sendMessage(color("&7Tippe &f/reports &7für eine Übersicht.")); - player.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - } - } - }, 2, TimeUnit.SECONDS); - } - - // ========================================================= - // DISCONNECT-EVENT: Cleanup + Leave-Nachricht - // ========================================================= - - @EventHandler - public void onDisconnect(PlayerDisconnectEvent e) { - ProxiedPlayer player = e.getPlayer(); - UUID uuid = player.getUniqueId(); - - // Leave-Nachricht - if (config.isJoinLeaveEnabled()) { - broadcastJoinLeave(player, false); - } - - // Cleanup - chatFilter.cleanup(uuid); - playerChannels.remove(uuid); - mentionsDisabled.remove(uuid); - awaitingInput.remove(uuid); - VanishProvider.cleanup(uuid); // Vanish-Status bereinigen - } - - // ========================================================= - // JOIN / LEAVE NACHRICHTEN (mit Vanish-Support) - // ========================================================= - - /** - * Sendet eine Join- oder Leave-Nachricht an alle Spieler. - * - * Vanish-Logik: - * - Unsichtbare Spieler: kein Broadcast an normale Spieler. - * - Ist vanish-show-to-admins=true, erhalten Admins (bypass-permission) - * eine dezente Vanish-Benachrichtigung. - * - * @param player Der betroffene Spieler - * @param isJoin true = Join, false = Leave - */ - private void broadcastJoinLeave(ProxiedPlayer player, boolean isJoin) { - boolean isVanished = VanishProvider.isVanished(player); - - String prefix = getLuckPermsPrefix(player); - String suffix = getLuckPermsSuffix(player); - String server = (player.getServer() != null) - ? config.getServerDisplay(player.getServer().getInfo().getName()) - : "Netzwerk"; - - String normalFormat = isJoin ? config.getJoinFormat() : config.getLeaveFormat(); - String vanishFormat = isJoin ? config.getVanishJoinFormat() : config.getVanishLeaveFormat(); - - // Platzhalter ersetzen - String normalMsg = normalFormat - .replace("{player}", player.getName()) - .replace("{prefix}", prefix != null ? prefix : "") - .replace("{suffix}", suffix != null ? suffix : "") - .replace("{server}", server); - - String vanishMsg = vanishFormat - .replace("{player}", player.getName()) - .replace("{prefix}", prefix != null ? prefix : "") - .replace("{suffix}", suffix != null ? suffix : "") - .replace("{server}", server); - - for (ProxiedPlayer recipient : ProxyServer.getInstance().getPlayers()) { - // Spieler sieht seine eigene Join-/Leave-Meldung nie - if (recipient.getUniqueId().equals(player.getUniqueId())) continue; - - // Admin = bypass-permission ODER notify-permission - boolean recipientIsAdmin = recipient.hasPermission(config.getAdminBypassPermission()) - || recipient.hasPermission(config.getAdminNotifyPermission()); - - if (isVanished) { - // Vanished: Nur Admins sehen eine dezente Meldung (wenn konfiguriert) - if (recipientIsAdmin && config.isVanishShowToAdmins()) { - recipient.sendMessage(color(vanishMsg)); - } - } else { - // Normaler Spieler: alle erhalten die Nachricht - // Vanished Admins sehen Join/Leave-Events anderer Spieler normal - recipient.sendMessage(color(normalMsg)); - } - } - - // Konsole immer informieren - String logMsg = isVanished - ? "[ChatModule] " + (isJoin ? "JOIN(V)" : "LEAVE(V)") + " " + player.getName() - : "[ChatModule] " + (isJoin ? "JOIN" : "LEAVE") + " " + player.getName(); - ProxyServer.getInstance().getConsole().sendMessage(color( - isVanished ? "&8" + logMsg : "&7" + logMsg)); - - // Brücken (nur für nicht-vanished Spieler) - if (!isVanished) { - String cleanMsg = ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', normalMsg)); - if (discordBridge != null && !config.getJoinLeaveDiscordWebhook().isEmpty()) { - discordBridge.sendToDiscord(config.getJoinLeaveDiscordWebhook(), - player.getName(), cleanMsg, null); - } - if (telegramBridge != null && !config.getJoinLeaveTelegramChatId().isEmpty()) { - telegramBridge.sendToTelegram(config.getJoinLeaveTelegramChatId(), - config.getJoinLeaveTelegramThreadId(), cleanMsg); - } - } - } - - // ========================================================= - // BEFEHLE REGISTRIEREN - // ========================================================= - - private void registerCommands() { - - // /channel | /ch - Command chCmd = new Command("channel", null, "ch", "kanal") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - if (args.length == 0) { - p.sendMessage(color("&8▸ &eVerfügbare Kanäle:")); - for (ChatChannel ch : config.getChannels().values()) { - boolean hasPerm = !ch.hasPermission() || p.hasPermission(ch.getPermission()); - String active = ch.getId().equals(playerChannels.getOrDefault(p.getUniqueId(), config.getDefaultChannelId())) ? " &a✔" : ""; - p.sendMessage(color( - " " + ch.getFormattedTag() - + " &f" + ch.getName() - + (hasPerm ? active : " &8(keine Berechtigung)"))); - } - return; - } - String target = args[0].toLowerCase(); - ChatChannel ch = config.getChannel(target); - if (ch == null) { p.sendMessage(color("&cKanal &f" + args[0] + " &cnicht gefunden.")); return; } - if (ch.hasPermission() && !p.hasPermission(ch.getPermission())) { - p.sendMessage(color("&cDu hast keine Berechtigung für diesen Kanal.")); return; - } - playerChannels.put(p.getUniqueId(), ch.getId()); - p.sendMessage(color("&aKanal gewechselt: " + ch.getFormattedTag() + " &a" + ch.getName())); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, chCmd); - - // /helpop - Command helpop = new Command("helpop") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - if (args.length == 0) { p.sendMessage(color("&cBenutzung: /helpop ")); return; } - - long now = System.currentTimeMillis() / 1000L; - Long last = helpopCooldowns.get(p.getUniqueId()); - if (last != null && (now - last) < config.getHelpopCooldown()) { - long wait = config.getHelpopCooldown() - (now - last); - p.sendMessage(color("&cBitte warte noch &f" + wait + "s &cvor dem nächsten HelpOp.")); - return; - } - helpopCooldowns.put(p.getUniqueId(), now); - - String msg = String.join(" ", args); - String server = p.getServer() != null ? p.getServer().getInfo().getName() : "Proxy"; - String formatted = buildSimpleFormat(config.getHelpopFormat(), - "player", p.getName(), "server", server, "message", msg); - - for (ProxiedPlayer op : ProxyServer.getInstance().getPlayers()) { - if (op.hasPermission(config.getHelpopPermission())) { - op.sendMessage(color(formatted)); - } - } - ProxyServer.getInstance().getConsole().sendMessage(color(formatted)); - p.sendMessage(color(config.getHelpopConfirm())); - - if (discordBridge != null && !config.getHelpopDiscordWebhook().isEmpty()) { - discordBridge.sendEmbedToDiscord(config.getHelpopDiscordWebhook(), - "🆘 HelpOp von " + p.getName() + " (" + server + ")", msg, "FFAA00"); - } - if (telegramBridge != null && !config.getHelpopTelegramChatId().isEmpty()) { - telegramBridge.sendFormattedToTelegram(config.getHelpopTelegramChatId(), - "🆘 HelpOp: " + p.getName() + " @" + server, msg); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, helpop); - - // /msg - // Vanish: Vanished Spieler sind für normale Spieler nicht erreichbar. - // Admins können vanished Spieler per PM kontaktieren. - Command msgCmd = new Command("msg", null, "tell", "w", "whisper") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!config.isPmEnabled()) { sender.sendMessage(color("&cPrivat-Nachrichten sind deaktiviert.")); return; } - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (args.length < 2) { sender.sendMessage(color("&cBenutzung: /msg ")); return; } - - ProxiedPlayer from = (ProxiedPlayer) sender; - boolean fromIsAdmin = from.hasPermission(config.getAdminBypassPermission()); - - // Ziel suchen – vanished Spieler sind für Nicht-Admins "nicht gefunden" - ProxiedPlayer to = findVisiblePlayer(args[0], fromIsAdmin); - if (to == null || !to.isConnected()) { - from.sendMessage(color("&cSpieler &f" + args[0] + " &cnicht gefunden.")); return; - } - - String message = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); - pmManager.send(from, to, message, config, config.getAdminBypassPermission()); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, msgCmd); - - // /r - Command replyCmd = new Command("r", null, "reply", "antwort") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!config.isPmEnabled()) { sender.sendMessage(color("&cPrivat-Nachrichten sind deaktiviert.")); return; } - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /r ")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - pmManager.reply(p, String.join(" ", args), config, config.getAdminBypassPermission()); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, replyCmd); - - // /ignore | /unignore - Command ignoreCmd = new Command("ignore", null, "block") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /ignore ")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(args[0]); - if (target == null) { p.sendMessage(color("&cSpieler nicht gefunden.")); return; } - if (target.hasPermission(config.getAdminBypassPermission())) { - p.sendMessage(color("&cAdmins können nicht ignoriert werden.")); return; - } - if (blockManager.isBlocked(p.getUniqueId(), target.getUniqueId())) { - p.sendMessage(color("&cDu hast &f" + target.getName() + " &cbereits ignoriert.")); return; - } - blockManager.block(p.getUniqueId(), target.getUniqueId()); - p.sendMessage(color("&aDu ignorierst jetzt &f" + target.getName() + "&a.")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, ignoreCmd); - - Command unignoreCmd = new Command("unignore", null, "unblock") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /unignore ")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(args[0]); - if (target == null) { - p.sendMessage(color("&cSpieler nicht online.")); - return; - } - if (!blockManager.isBlocked(p.getUniqueId(), target.getUniqueId())) { - p.sendMessage(color("&cDu hast diesen Spieler nicht ignoriert.")); return; - } - blockManager.unblock(p.getUniqueId(), target.getUniqueId()); - p.sendMessage(color("&aIgnore für &f" + args[0] + " &aaufgehoben.")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, unignoreCmd); - - // /chatmute | /chatunmute - Command muteCmd = new Command("chatmute", "chat.mute", "gmute") { - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /chatmute [Minuten]")); return; } - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(args[0]); - if (target == null) { sender.sendMessage(color("&cSpieler &f" + args[0] + " &cist nicht online.")); return; } - if (target.hasPermission(config.getAdminBypassPermission())) { - sender.sendMessage(color("&cDieser Spieler kann nicht gemutet werden.")); return; - } - int duration = config.getDefaultMuteDuration(); - if (args.length >= 2) { - try { duration = Integer.parseInt(args[1]); } - catch (NumberFormatException ex) { sender.sendMessage(color("&cUngültige Dauer. Bitte Zahl eingeben.")); return; } - } - muteManager.mute(target.getUniqueId(), duration); - String durationStr = duration <= 0 ? "permanent" : duration + " Minuten"; - target.sendMessage(color("&cDu wurdest für " + durationStr + " stummgeschaltet.")); - sender.sendMessage(color("&a" + target.getName() + " wurde für " + durationStr + " gemutet.")); - notifyAdmins("&8[&cMute&8] &f" + (sender instanceof ProxiedPlayer ? sender.getName() : "Konsole") - + " &7hat &f" + target.getName() + " &7für &f" + durationStr + " &7gemutet."); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, muteCmd); - - Command unmuteCmd = new Command("chatunmute", "chat.mute", "gunmute") { - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /chatunmute ")); return; } - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(args[0]); - if (target == null) { sender.sendMessage(color("&cSpieler nicht online.")); return; } - if (!muteManager.isMuted(target.getUniqueId())) { - sender.sendMessage(color("&cDieser Spieler ist nicht gemutet.")); return; - } - muteManager.unmute(target.getUniqueId()); - target.sendMessage(color("&aDeine Stummschaltung wurde aufgehoben.")); - sender.sendMessage(color("&a" + target.getName() + " wurde entmutet.")); - notifyAdmins("&8[&aUnmute&8] &f" + (sender instanceof ProxiedPlayer ? sender.getName() : "Konsole") - + " &7hat &f" + target.getName() + " &7entmutet."); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, unmuteCmd); - - // /chataus (Selbst-Mute) - Command selfMuteCmd = new Command("chataus", null, "togglechat", "chaton", "chatoff") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - if (selfChatMuted.contains(p.getUniqueId())) { - selfChatMuted.remove(p.getUniqueId()); - p.sendMessage(color("&aChat &l✔ eingeschaltet.")); - } else { - selfChatMuted.add(p.getUniqueId()); - p.sendMessage(color("&cChat &l✘ ausgeschaltet. &7Mit &f/chataus &7wieder einschalten.")); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, selfMuteCmd); - - // /broadcast - Command broadcastCmd = new Command("broadcast", config.getBroadcastPermission(), "bc", "alert") { - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /broadcast ")); return; } - String message = String.join(" ", args); - String formatted = config.getBroadcastFormat().replace("{message}", message); - ProxyServer.getInstance().broadcast(color(formatted)); - - if (discordBridge != null) { - if (!config.getDiscordAdminChannelId().isEmpty()) { - discordBridge.sendToChannel(config.getDiscordAdminChannelId(), - "📢 **Broadcast:** " + ChatColor.stripColor( - ChatColor.translateAlternateColorCodes('&', message))); - } - } - if (telegramBridge != null && !config.getTelegramAdminChatId().isEmpty()) { - telegramBridge.sendFormattedToTelegram(config.getTelegramAdminChatId(), - "📢 Broadcast", - ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', message))); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, broadcastCmd); - - // /emoji - Command emojiCmd = new Command("emoji", null, "emojis") { - @Override - public void execute(CommandSender sender, String[] args) { - sender.sendMessage(color(emojiParser.buildEmojiList())); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, emojiCmd); - - // /socialspy - Command spyCmd = new Command("socialspy", "chat.socialspy", "spy") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - boolean now = pmManager.toggleSpy(p.getUniqueId()); - p.sendMessage(color(now ? "&aSocial-Spy aktiviert." : "&cSocial-Spy deaktiviert.")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, spyCmd); - - // /chatreload - Command reloadCmd = new Command("chatreload", "chat.admin.bypass") { - @Override - public void execute(CommandSender sender, String[] args) { - config.load(); - emojiParser = new EmojiParser(config.getEmojiMappings(), config.isEmojiEnabled()); - chatFilter = new ChatFilter(config.getFilterConfig()); - sender.sendMessage(color("&aChat-Konfiguration neu geladen.")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, reloadCmd); - - // /chatinfo – Admin-Info über einen Spieler - Command chatInfoCmd = new Command("chatinfo", "chat.admin.bypass") { - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /chatinfo ")); return; } - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(args[0]); - if (target == null) { sender.sendMessage(color("&cSpieler &f" + args[0] + " &cnicht online.")); return; } - - UUID tUUID = target.getUniqueId(); - String tServer = target.getServer() != null ? target.getServer().getInfo().getName() : "Proxy"; - - String channelId = playerChannels.getOrDefault(tUUID, config.getDefaultChannelId()); - ChatChannel ch = config.getChannel(channelId); - String channelName = ch != null ? ch.getFormattedTag() + " &f" + ch.getName() : "&f" + channelId; - - String muteStatus = muteManager.isMuted(tUUID) - ? "&cJa &8(noch: &f" + muteManager.getRemainingTime(tUUID) + "&8)" - : "&aKein"; - - Set blocked = blockManager.getBlockedBy(tUUID); - - AccountLinkManager.LinkedAccount link = linkManager.getByUUID(tUUID); - String discordInfo = (link != null && !link.discordUserId.isEmpty()) - ? "&a" + link.discordUsername + " &8(" + link.discordUserId + ")" : "&7Nicht verknüpft"; - String telegramInfo = (link != null && !link.telegramUserId.isEmpty()) - ? "&a" + link.telegramUsername + " &8(" + link.telegramUserId + ")" : "&7Nicht verknüpft"; - - String vanishStatus = VanishProvider.isVanished(target) ? "&eJa" : "&aKein"; - - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - sender.sendMessage(color("&eChatInfo: &f" + target.getName() + " &8@ &7" + tServer)); - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - sender.sendMessage(color("&7Kanal: " + channelName)); - sender.sendMessage(color("&7Mute: " + muteStatus)); - sender.sendMessage(color("&7Chat-aus: " + (selfChatMuted.contains(tUUID) ? "&cJa" : "&aKein"))); - sender.sendMessage(color("&7Mentions: " + (mentionsDisabled.contains(tUUID) ? "&cDeaktiviert" : "&aAktiv"))); - sender.sendMessage(color("&7Vanish: " + vanishStatus)); - sender.sendMessage(color("&7Blockiert: &f" + blocked.size() + " Spieler")); - if (!blocked.isEmpty()) { - for (UUID bUUID : blocked) { - ProxiedPlayer bp = ProxyServer.getInstance().getPlayer(bUUID); - String bName = bp != null ? bp.getName() : bUUID.toString().substring(0, 8) + "..."; - sender.sendMessage(color(" &8- &7" + bName)); - } - } - sender.sendMessage(color("&7Discord: " + discordInfo)); - sender.sendMessage(color("&7Telegram: " + telegramInfo)); - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, chatInfoCmd); - - // /chathist [spieler] [anzahl] – Chat-History aus dem Logfile - Command chatHistCmd = new Command("chathist", "chat.admin.bypass") { - @Override - public void execute(CommandSender sender, String[] args) { - if (chatLogger == null) { - sender.sendMessage(color("&cChat-Log ist deaktiviert.")); return; - } - - String playerFilter = null; - int lines = config.getHistoryDefaultLines(); - - if (args.length >= 1) { - try { - lines = Math.min(Integer.parseInt(args[0]), config.getHistoryMaxLines()); - } catch (NumberFormatException ex) { - playerFilter = args[0]; - } - } - if (args.length >= 2) { - try { - lines = Math.min(Integer.parseInt(args[1]), config.getHistoryMaxLines()); - } catch (NumberFormatException ignored) {} - } - - final String finalFilter = playerFilter; - final int finalLines = lines; - - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - List history = chatLogger.readLastLines(finalFilter, finalLines); - - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - sender.sendMessage(color("&eChatHistory &8| &f" + history.size() + " Zeilen" - + (finalFilter != null ? " &8| &7Spieler: &f" + finalFilter : ""))); - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - - if (history.isEmpty()) { - sender.sendMessage(color("&7Keine Einträge gefunden.")); - } else { - for (String line : history) { - sender.sendMessage(color("&8" + line)); - } - } - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - }); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, chatHistCmd); - - // /mentions – Mentions ein-/ausschalten - Command mentionsCmd = new Command("mentions", null, "mention") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (!config.isMentionsEnabled()) { sender.sendMessage(color("&cMentions sind deaktiviert.")); return; } - if (!config.isMentionsAllowToggle()) { sender.sendMessage(color("&cDas Deaktivieren von Mentions ist nicht erlaubt.")); return; } - - ProxiedPlayer p = (ProxiedPlayer) sender; - if (mentionsDisabled.contains(p.getUniqueId())) { - mentionsDisabled.remove(p.getUniqueId()); - p.sendMessage(color("&aMentions &l✔ &aaktiviert. Du wirst benachrichtigt wenn jemand @" + p.getName() + " schreibt.")); - } else { - mentionsDisabled.add(p.getUniqueId()); - p.sendMessage(color("&cMentions &l✘ &cdeaktiviert. Du wirst nicht mehr benachrichtigt.")); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, mentionsCmd); - - // /chatbypass – Chat-Verarbeitung für nächste Eingabe(n) überspringen - Command bypassCmd = new Command("chatbypass", null, "cbp") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - - if (awaitingInput.contains(p.getUniqueId())) { - awaitingInput.remove(p.getUniqueId()); - p.sendMessage(color("&aChatModule &l✔ &aaktiv.")); - } else { - awaitingInput.add(p.getUniqueId()); - p.sendMessage(color("&eChatModule &l⏸ &eüberbrückt. &7Nächste Nachricht geht direkt an den Server.")); - p.sendMessage(color("&7Mit &f/chatbypass &7wieder einschalten.")); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, bypassCmd); - - // /discordlink – Discord-Account verknüpfen - Command discordLinkCmd = new Command("discordlink", null, "dlink") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (!config.isDiscordEnabled()) { sender.sendMessage(color("&cDiscord ist nicht aktiviert.")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - - String token = linkManager.generateToken(p.getUniqueId(), p.getName(), "discord"); - - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - p.sendMessage(color("&9&lDiscord-Verknüpfung")); - p.sendMessage(color("&7Schreibe dem Bot auf Discord:")); - p.sendMessage(color("&f!link &b" + token)); - p.sendMessage(color("&7Token gültig für &f10 Minuten&7.")); - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, discordLinkCmd); - - // /telegramlink – Telegram-Account verknüpfen - Command telegramLinkCmd = new Command("telegramlink", null, "tlink") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - if (!config.isTelegramEnabled()) { sender.sendMessage(color("&cTelegram ist nicht aktiviert.")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - - String token = linkManager.generateToken(p.getUniqueId(), p.getName(), "telegram"); - - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - p.sendMessage(color("&3&lTelegram-Verknüpfung")); - p.sendMessage(color("&7Schreibe dem Bot auf Telegram:")); - p.sendMessage(color("&f/link &b" + token)); - p.sendMessage(color("&7Token gültig für &f10 Minuten&7.")); - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, telegramLinkCmd); - - // /unlink – Verknüpfung aufheben - Command unlinkCmd = new Command("unlink") { - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - ProxiedPlayer p = (ProxiedPlayer) sender; - - if (args.length == 0) { - p.sendMessage(color("&cBenutzung: /unlink ")); - return; - } - - switch (args[0].toLowerCase()) { - case "discord": - if (linkManager.unlinkDiscord(p.getUniqueId())) - p.sendMessage(color("&aDiscord-Verknüpfung aufgehoben.")); - else - p.sendMessage(color("&cKein Discord-Account verknüpft.")); - break; - case "telegram": - if (linkManager.unlinkTelegram(p.getUniqueId())) - p.sendMessage(color("&aTelegram-Verknüpfung aufgehoben.")); - else - p.sendMessage(color("&cKein Telegram-Account verknüpft.")); - break; - case "all": - boolean d = linkManager.unlinkDiscord(p.getUniqueId()); - boolean t = linkManager.unlinkTelegram(p.getUniqueId()); - if (d || t) p.sendMessage(color("&aAlle Verknüpfungen aufgehoben.")); - else p.sendMessage(color("&cKeine Verknüpfungen vorhanden.")); - break; - default: - p.sendMessage(color("&cBenutzung: /unlink ")); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, unlinkCmd); - - // /report - Command reportCmd = new Command("report") { - @Override - public void execute(CommandSender sender, String[] args) { - if (reportManager == null) { sender.sendMessage(color("&cDas Report-System ist deaktiviert.")); return; } - if (!(sender instanceof ProxiedPlayer)) { sender.sendMessage(color("&cNur Spieler!")); return; } - - ProxiedPlayer p = (ProxiedPlayer) sender; - - String reqPerm = config.getReportPermission(); - if (reqPerm != null && !reqPerm.isEmpty() && !p.hasPermission(reqPerm)) { - p.sendMessage(color("&cDu hast keine Berechtigung für /report.")); return; - } - - if (args.length < 2) { - p.sendMessage(color("&cBenutzung: /report ")); - return; - } - - long now = System.currentTimeMillis() / 1000L; - Long last = reportCooldowns.get(p.getUniqueId()); - if (last != null && (now - last) < config.getReportCooldown()) { - long wait = config.getReportCooldown() - (now - last); - p.sendMessage(color("&cBitte warte noch &f" + wait + "s &cvor dem nächsten Report.")); - return; - } - - String reportedName = args[0]; - String reason = String.join(" ", Arrays.copyOfRange(args, 1, args.length)); - String server = p.getServer() != null ? p.getServer().getInfo().getName() : "Proxy"; - String msgContext = lastChatMessages.getOrDefault(reportedName.toLowerCase(), "(keine Chat-Nachricht bekannt)"); - - String reportId = reportManager.createReport( - p.getName(), p.getUniqueId(), reportedName, server, msgContext, reason); - - if (chatLogger != null) { - String logMsg = "[REPORT] Reporter: " + p.getName() + ", Gemeldet: " + reportedName - + ", Grund: " + reason + " | Letzte Nachricht: " + msgContext - + " | Report-ID: " + reportId; - chatLogger.log(reportId, server, "report", p.getName(), logMsg); - } - - String reportWebhook = config.getReportDiscordWebhook(); - if (config.isReportWebhookEnabled() && discordBridge != null - && reportWebhook != null && !reportWebhook.isEmpty()) { - String title = "Neuer Report eingegangen"; - String desc = "**Reporter:** " + p.getName() - + "\n**Gemeldet:** " + reportedName - + "\n**Server:** " + server - + "\n**Grund:** " + reason - + "\n**Letzte Nachricht:** " + msgContext - + "\n**Report-ID:** " + reportId; - discordBridge.sendEmbedToDiscord(reportWebhook, title, desc, config.getDiscordEmbedColor()); - } - - String reportTgChatId = config.getReportTelegramChatId(); - if (telegramBridge != null && reportTgChatId != null && !reportTgChatId.isEmpty()) { - String header = "Neuer Report eingegangen"; - String content = "Reporter: " + p.getName() - + "\nGemeldet: " + reportedName - + "\nServer: " + server - + "\nGrund: " + reason - + "\nLetzte Nachricht: " + msgContext - + "\nReport-ID: " + reportId; - telegramBridge.sendFormattedToTelegram(reportTgChatId, header, content); - } - - reportCooldowns.put(p.getUniqueId(), now); - - String confirm = config.getReportConfirm().replace("{id}", reportId); - p.sendMessage(color(confirm)); - - notifyAdminsReport(reportId, p.getName(), reportedName, server, reason, msgContext); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, reportCmd); - - // /reports [all] – Admin-Übersicht - Command reportsCmd = new Command("reports", config.getReportViewPermission()) { - @Override - public void execute(CommandSender sender, String[] args) { - if (reportManager == null) { sender.sendMessage(color("&cDas Report-System ist deaktiviert.")); return; } - - boolean showAll = args.length > 0 && args[0].equalsIgnoreCase("all"); - List list = showAll - ? reportManager.getAllReports() - : reportManager.getOpenReports(); - - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - sender.sendMessage(color("&c&l⚑ REPORTS &8| &f" + list.size() - + (showAll ? " gesamt" : " offen") - + " &8| &7/reports all für alle")); - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - - if (list.isEmpty()) { - sender.sendMessage(color("&7Keine " + (showAll ? "" : "offenen ") + "Reports vorhanden.")); - return; - } - - for (ReportManager.ChatReport r : list) { - String statusColor = r.closed ? "&a✔" : "&c✘"; - - if (sender instanceof ProxiedPlayer) { - ComponentBuilder line = new ComponentBuilder(""); - - line.append(ChatColor.translateAlternateColorCodes('&', statusColor + " ")) - .event((ClickEvent) null) - .event((HoverEvent) null); - - line.append(ChatColor.translateAlternateColorCodes('&', "&8[&f" + r.id + "&8]")) - .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, r.id)) - .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - new ComponentBuilder(ChatColor.GRAY + "Klicken zum Kopieren: " + r.id - + "\n" + ChatColor.YELLOW + "Reporter: " + ChatColor.WHITE + r.reporterName - + "\n" + ChatColor.YELLOW + "Gemeldet: " + ChatColor.RED + r.reportedName - + "\n" + ChatColor.YELLOW + "Server: " + ChatColor.GREEN + r.server - + "\n" + ChatColor.YELLOW + "Zeit: " + ChatColor.WHITE + r.getFormattedTime() - + "\n" + ChatColor.YELLOW + "Kontext: " + ChatColor.GRAY + r.messageContext - + "\n" + ChatColor.YELLOW + "Grund: " + ChatColor.RED + r.reason - + (r.closed ? "\n" + ChatColor.GREEN + "Geschlossen von: " + r.closedBy : "")).create())); - - line.append(ChatColor.translateAlternateColorCodes('&', - " &b" + r.reportedName + " &8← &7" + r.reporterName - + " &8@ &a" + r.server - + " &8| &e" + r.getFormattedTime())) - .event((ClickEvent) null) - .event((HoverEvent) null); - - ((ProxiedPlayer) sender).sendMessage(line.create()); - } else { - sender.sendMessage(color(statusColor + " &8[&f" + r.id + "&8] &b" + r.reportedName - + " &8← &7" + r.reporterName + " &8@ &a" + r.server - + " &8| &e" + r.getFormattedTime() - + " &8| &cGrund: &f" + r.reason)); - } - } - - sender.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - if (!showAll && sender instanceof ProxiedPlayer) { - sender.sendMessage(color("&7Tipp: &f/reportclose &7zum Schließen.")); - } - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, reportsCmd); - - // /reportclose - Command reportCloseCmd = new Command("reportclose", config.getReportClosePermission()) { - @Override - public void execute(CommandSender sender, String[] args) { - if (reportManager == null) { sender.sendMessage(color("&cDas Report-System ist deaktiviert.")); return; } - if (args.length == 0) { sender.sendMessage(color("&cBenutzung: /reportclose ")); return; } - - String id = args[0].toUpperCase(); - ReportManager.ChatReport report = reportManager.getReport(id); - - if (report == null) { - sender.sendMessage(color("&cReport &f" + id + " &cnicht gefunden.")); return; - } - if (report.closed) { - sender.sendMessage(color("&cReport &f" + id + " &cist bereits geschlossen" - + (report.closedBy != null && !report.closedBy.isEmpty() - ? " &8(von &7" + report.closedBy + "&8)" : "") + ".")); - return; - } - - String adminName = (sender instanceof ProxiedPlayer) ? sender.getName() : "Konsole"; - reportManager.closeReport(id, adminName); - - sender.sendMessage(color("&aReport &f" + id + " &awurde geschlossen.")); - - ProxiedPlayer reporter = ProxyServer.getInstance().getPlayer(report.reporterUUID); - if (reporter != null && reporter.isConnected()) { - reporter.sendMessage(color("&8[&aReport&8] &7Dein Report &f" + id - + " &7gegen &c" + report.reportedName + " &7wurde von &b" + adminName + " &7bearbeitet.")); - } - - notifyAdmins("&8[&aReport geschlossen&8] &f" + adminName + " &7hat Report &f" + id + " &7geschlossen."); - } - }; - ProxyServer.getInstance().getPluginManager().registerCommand(plugin, reportCloseCmd); - } - - // ========================================================= - // PLUGIN-INPUT BYPASS - // ========================================================= - - /** - * Öffentliche API für Sub-Server-Plugins oder BungeeCord-eigene Plugins. - * Setzt den Bypass-Status für einen Spieler. - * - * Beispiel aus einem anderen BungeeCord-Plugin: - * ChatModule chatModule = (ChatModule) proxy.getPluginManager() - * .getPlugin("StatusAPI").getModule("ChatModule"); - * chatModule.setAwaitingInput(player.getUniqueId(), true); - */ - public void setAwaitingInput(UUID uuid, boolean awaiting) { - if (awaiting) awaitingInput.add(uuid); - else awaitingInput.remove(uuid); - } - - public boolean isAwaitingInput(UUID uuid) { - return awaitingInput.contains(uuid); - } - - // ========================================================= - // VANISH-HILFSMETHODEN - // ========================================================= - - /** - * Sucht einen Spieler nach Name und berücksichtigt den Vanish-Status. - * - * @param name Spielername (case-insensitiv) - * @param callerIsAdmin true → Vanished Spieler werden ebenfalls gefunden - * @return ProxiedPlayer oder null wenn nicht gefunden / vanished (für Nicht-Admins) - */ - private ProxiedPlayer findVisiblePlayer(String name, boolean callerIsAdmin) { - ProxiedPlayer target = ProxyServer.getInstance().getPlayer(name); - if (target == null) return null; - if (!callerIsAdmin && VanishProvider.isVanished(target)) return null; - return target; - } - - // ========================================================= - // HILFSMETHODEN - // ========================================================= - - private String buildFormat(String format, String server, String prefix, - String player, String suffix, String message) { - String serverColor = config.getServerColor(server); - String serverDisplay = config.getServerDisplay(server); - // Nur den Servernamen-Teil vorübersetzen damit &#RRGGBB im Display-Namen - // korrekt sitzt; der Rest wird am Ausgabepunkt via translateColors() übersetzt. - String coloredServer = serverColor + serverDisplay + "&r"; - - return format - .replace("{server}", coloredServer) - .replace("{prefix}", prefix != null ? prefix : "") - .replace("{player}", player) - .replace("{suffix}", suffix != null ? suffix : "") - .replace("{message}", message); - } - - private String buildSimpleFormat(String format, String... kvPairs) { - String result = format; - for (int i = 0; i + 1 < kvPairs.length; i += 2) { - result = result.replace("{" + kvPairs[i] + "}", kvPairs[i + 1]); - } - return result; - } - - private TextComponent color(String text) { - return new TextComponent(translateColors(text)); - } - - /** - * Übersetzt sowohl klassische &-Farbcodes als auch HEX-Codes im Format &#RRGGBB. - */ - private String translateColors(String text) { - if (text == null) return ""; - - StringBuilder sb = new StringBuilder(); - int i = 0; - while (i < text.length()) { - if (i + 7 < text.length() - && text.charAt(i) == '&' - && text.charAt(i + 1) == '#') { - String hex = text.substring(i + 2, i + 8); - boolean validHex = hex.chars().allMatch(c -> - (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); - if (validHex) { - try { - sb.append(ChatColor.of("#" + hex).toString()); - i += 8; - continue; - } catch (Exception ignored) { - // Ungültige Farbe → als normalen Text behandeln - } - } - } - sb.append(text.charAt(i)); - i++; - } - - return ChatColor.translateAlternateColorCodes('&', sb.toString()); - } - - private void notifyAdmins(String message) { - for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { - if (p.hasPermission(config.getAdminNotifyPermission())) { - p.sendMessage(color(message)); - } - } - } - - /** - * Benachrichtigt alle online Admins über einen neuen Report. - */ - private void notifyAdminsReport(String reportId, String reporter, String reported, - String server, String reason, String msgContext) { - java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); - String zeit = sdf.format(new java.util.Date()); - - for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) { - if (!p.hasPermission(config.getAdminNotifyPermission()) - && !p.hasPermission(config.getAdminBypassPermission())) continue; - - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - p.sendMessage(color("&8[&cReport&8] &7Zeit: &e" + zeit)); - p.sendMessage(color("&7Reporter: &b" + reporter)); - p.sendMessage(color("&7Server: &a" + server)); - p.sendMessage(color("&7Letzte Nachricht: &f" + msgContext)); - p.sendMessage(color("&7Grund: &c" + reason)); - - ComponentBuilder idLine = new ComponentBuilder(ChatColor.GRAY + "ID: "); - idLine.append(ChatColor.WHITE + "" + ChatColor.BOLD + reportId) - .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, reportId)) - .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - new ComponentBuilder(ChatColor.GRAY + "Klicken zum Kopieren · " - + ChatColor.YELLOW + "/reportclose " + reportId).create())) - .append(ChatColor.GRAY + " (klicken zum Kopieren)", ComponentBuilder.FormatRetention.NONE) - .event((ClickEvent) null) - .event((HoverEvent) null); - p.sendMessage(idLine.create()); - - p.sendMessage(color("&8▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")); - } - - ProxyServer.getInstance().getConsole().sendMessage(color( - "&8[&cReport " + reportId + "&8] &7Reporter: &b" + reporter - + " &7→ &c" + reported + " &7@ &a" + server + " &8| &cGrund: &f" + reason)); - } - - /** - * Baut eine BungeeCord-Nachricht mit klickbarem [⚑] Melden-Button. - */ - private BaseComponent[] buildClickableMessage(String msgId, String formatted, String senderName) { - ComponentBuilder builder = new ComponentBuilder(""); - - builder.append(translateColors(formatted), - ComponentBuilder.FormatRetention.NONE) - .event((ClickEvent) null) - .event((HoverEvent) null); - - if (msgId != null && senderName != null && reportManager != null) { - builder.append(" ", ComponentBuilder.FormatRetention.NONE) - .event((ClickEvent) null) - .event((HoverEvent) null); - builder.append(ChatColor.DARK_GRAY + "[" + ChatColor.RED + "⚑" + ChatColor.DARK_GRAY + "]", - ComponentBuilder.FormatRetention.NONE) - .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, - "/report " + senderName + " ")) - .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, - new ComponentBuilder(ChatColor.GRAY + "Spieler melden\n" - + ChatColor.YELLOW + "/report " + senderName + " ").create())); - } - - return builder.create(); - } - - private boolean isBedrock(ProxiedPlayer player) { - return player.getName().startsWith(GEYSER_PREFIX); - } - - /** - * Sendet einen Sound-Hinweis via Actionbar (Mention-Feedback). - */ - private void sendMentionSound(ProxiedPlayer player, String soundName) { - if (soundName == null || soundName.isEmpty()) return; - try { - net.md_5.bungee.api.chat.TextComponent actionBar = - new net.md_5.bungee.api.chat.TextComponent( - ChatColor.translateAlternateColorCodes('&', "&e♪ Mention!")); - player.sendMessage(net.md_5.bungee.api.ChatMessageType.ACTION_BAR, actionBar); - } catch (Exception ignored) {} - } - - private String getLuckPermsPrefix(ProxiedPlayer player) { - try { - net.luckperms.api.LuckPerms lp = net.luckperms.api.LuckPermsProvider.get(); - net.luckperms.api.model.user.User user = lp.getUserManager().getUser(player.getUniqueId()); - if (user == null) return ""; - String prefix = user.getCachedData().getMetaData().getPrefix(); - if (prefix == null) return ""; - return ChatColor.translateAlternateColorCodes('&', prefix); - } catch (Exception e) { - return ""; - } - } - - private String getLuckPermsSuffix(ProxiedPlayer player) { - try { - net.luckperms.api.LuckPerms lp = net.luckperms.api.LuckPermsProvider.get(); - net.luckperms.api.model.user.User user = lp.getUserManager().getUser(player.getUniqueId()); - if (user == null) return ""; - String suffix = user.getCachedData().getMetaData().getSuffix(); - if (suffix == null) return ""; - return ChatColor.translateAlternateColorCodes('&', suffix); - } catch (Exception e) { - return ""; - } - } - - // ========================================================= - // EXTERNE BRÜCKEN - // ========================================================= - - private void bridgeToDiscord(ChatChannel channel, String playerName, String message, String server) { - if (discordBridge == null) return; - String cleanMessage = "[" + server + "] " + playerName + ": " - + ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', message)); - if (!channel.getDiscordWebhook().isEmpty()) { - discordBridge.sendToDiscord(channel.getDiscordWebhook(), playerName, cleanMessage, null); - } - if (!channel.getDiscordChannelId().isEmpty()) { - discordBridge.sendToChannel(channel.getDiscordChannelId(), cleanMessage); - } - if (channel.isUseAdminBridge() && !config.getDiscordAdminChannelId().isEmpty()) { - discordBridge.sendToChannel(config.getDiscordAdminChannelId(), cleanMessage); - } - } - - private void bridgeToTelegram(ChatChannel channel, String playerName, String message, String server) { - if (telegramBridge == null) return; - String cleanMessage = "[" + server + "] " + playerName + ": " - + ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', message)); - if (!channel.getTelegramChatId().isEmpty()) { - telegramBridge.sendToTelegram(channel.getTelegramChatId(), - channel.getTelegramThreadId(), cleanMessage); - } - if (channel.isUseAdminBridge() && !config.getTelegramAdminChatId().isEmpty()) { - telegramBridge.sendToTelegram(config.getTelegramAdminChatId(), - config.getTelegramAdminTopicId(), cleanMessage); - } - } -} \ No newline at end of file