From 951bacfc2ab14f2156ef5cc9f77989304591d0b6 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Fri, 22 May 2026 17:25:35 +0000 Subject: [PATCH] Delete src/main/java/net/viper/status/modules/chat/bridge/DiscordBridge.java via Git Manager GUI --- .../modules/chat/bridge/DiscordBridge.java | 323 ------------------ 1 file changed, 323 deletions(-) delete mode 100644 src/main/java/net/viper/status/modules/chat/bridge/DiscordBridge.java diff --git a/src/main/java/net/viper/status/modules/chat/bridge/DiscordBridge.java b/src/main/java/net/viper/status/modules/chat/bridge/DiscordBridge.java deleted file mode 100644 index 6d42903..0000000 --- a/src/main/java/net/viper/status/modules/chat/bridge/DiscordBridge.java +++ /dev/null @@ -1,323 +0,0 @@ -package net.viper.status.modules.chat.bridge; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.plugin.Plugin; -import net.viper.status.modules.chat.AccountLinkManager; -import net.viper.status.modules.chat.ChatConfig; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; - -/** - * Discord-Brücke für bidirektionale Kommunikation. - * - * Fix #12: extractJsonString() behandelt Escape-Sequenzen jetzt korrekt. - * Statt Zeichenvergleich mit dem Vorgänger-Char wird ein expliziter Escape-Flag verwendet. - */ -public class DiscordBridge { - - private final Plugin plugin; - private final ChatConfig config; - private final Logger logger; - private AccountLinkManager linkManager; - - private final java.util.Map lastMessageIds = new java.util.concurrent.ConcurrentHashMap<>(); - private volatile boolean running = false; - - public DiscordBridge(Plugin plugin, ChatConfig config) { - this.plugin = plugin; - this.config = config; - this.logger = plugin.getLogger(); - } - - public void setLinkManager(AccountLinkManager linkManager) { this.linkManager = linkManager; } - - public void start() { - if (!config.isDiscordEnabled() - || config.getDiscordBotToken().isEmpty() - || config.getDiscordBotToken().equals("YOUR_BOT_TOKEN_HERE")) { - logger.warning("[ChatModule-Discord] Bot-Token nicht konfiguriert. Discord-Empfang deaktiviert."); - return; - } - running = true; - int interval = Math.max(2, config.getDiscordPollInterval()); - plugin.getProxy().getScheduler().schedule(plugin, this::pollAllChannels, interval, interval, TimeUnit.SECONDS); - logger.info("[ChatModule-Discord] Brücke gestartet (Poll-Intervall: " + interval + "s)."); - } - - public void stop() { running = false; } - - // ===== Minecraft → Discord ===== - - public void sendToDiscord(String webhookUrl, String username, String message, String avatarUrl) { - if (webhookUrl == null || webhookUrl.isEmpty()) return; - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - try { - String payload = "{\"username\":\"" + escapeJson(username) + "\"" - + (avatarUrl != null && !avatarUrl.isEmpty() ? ",\"avatar_url\":\"" + avatarUrl + "\"" : "") - + ",\"content\":\"" + escapeJson(message) + "\"}"; - postJson(webhookUrl, payload, null); - } catch (Exception e) { - logger.warning("[ChatModule-Discord] Webhook-Fehler: " + e.getMessage()); - } - }); - } - - public void sendEmbedToDiscord(String webhookUrl, String title, String description, String colorHex) { - if (webhookUrl == null || webhookUrl.isEmpty()) return; - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - try { - int color = 0x5865F2; - try { color = Integer.parseInt(colorHex.replace("#", ""), 16); } catch (Exception ignored) {} - String payload = "{\"embeds\":[{\"title\":\"" + escapeJson(title) + "\"" - + ",\"description\":\"" + escapeJson(description) + "\"" - + ",\"color\":" + color + "}]}"; - postJson(webhookUrl, payload, null); - } catch (Exception e) { - logger.warning("[ChatModule-Discord] Embed-Fehler: " + e.getMessage()); - } - }); - } - - public void sendToChannel(String channelId, String message) { - if (channelId == null || channelId.isEmpty()) return; - if (config.getDiscordBotToken().isEmpty()) return; - plugin.getProxy().getScheduler().runAsync(plugin, () -> { - try { - String url = "https://discord.com/api/v10/channels/" + channelId + "/messages"; - postJson(url, "{\"content\":\"" + escapeJson(message) + "\"}", "Bot " + config.getDiscordBotToken()); - } catch (Exception e) { - logger.warning("[ChatModule-Discord] Send-to-Channel-Fehler: " + e.getMessage()); - } - }); - } - - // ===== Discord → Minecraft (Polling) ===== - - private void pollAllChannels() { - if (!running) return; - java.util.Set channelIds = new java.util.LinkedHashSet<>(); - for (net.viper.status.modules.chat.ChatChannel ch : config.getChannels().values()) { - if (!ch.getDiscordChannelId().isEmpty()) channelIds.add(ch.getDiscordChannelId()); - } - if (!config.getDiscordAdminChannelId().isEmpty()) channelIds.add(config.getDiscordAdminChannelId()); - for (String channelId : channelIds) pollChannel(channelId); - } - - private void pollChannel(String channelId) { - try { - AtomicLong lastId = lastMessageIds.computeIfAbsent(channelId, k -> new AtomicLong(0L)); - if (lastId.get() == 0L) { - String initResp = getJson("https://discord.com/api/v10/channels/" + channelId + "/messages?limit=1", - "Bot " + config.getDiscordBotToken()); - if (initResp != null && !initResp.equals("[]") && !initResp.isEmpty()) { - java.util.List initMsgs = parseMessages(initResp); - if (!initMsgs.isEmpty()) lastId.set(initMsgs.get(0).id); - } - return; - } - - String url = "https://discord.com/api/v10/channels/" + channelId + "/messages?after=" + lastId.get() + "&limit=10"; - String response = getJson(url, "Bot " + config.getDiscordBotToken()); - if (response == null || response.equals("[]") || response.isEmpty()) return; - - java.util.List messages = parseMessages(response); - messages.sort(java.util.Comparator.comparingLong(m -> m.id)); - - for (DiscordMessage msg : messages) { - if (msg.id <= lastId.get()) continue; - if (msg.isBot) continue; - if (msg.content.isEmpty()) continue; - lastId.set(msg.id); - - if (msg.content.startsWith("!link ")) { - String token = msg.content.substring(6).trim().toUpperCase(); - if (linkManager != null) { - AccountLinkManager.LinkedAccount acc = linkManager.redeemDiscord(token, msg.authorId, msg.authorName); - if (acc != null) sendToChannel(channelId, "✅ Verknüpfung erfolgreich! Minecraft-Account: **" + acc.minecraftName + "**"); - else sendToChannel(channelId, "❌ Ungültiger oder abgelaufener Token. Bitte `/discordlink` im Spiel erneut ausführen."); - } - continue; - } - - String displayName = (linkManager != null) - ? linkManager.resolveDiscordName(msg.authorId, msg.authorName) : msg.authorName; - String mcFormat = resolveFormat(channelId); - if (mcFormat == null) continue; - - String formatted = ChatColor.translateAlternateColorCodes('&', - mcFormat.replace("{user}", displayName).replace("{message}", msg.content)); - ProxyServer.getInstance().getScheduler().runAsync(plugin, - () -> ProxyServer.getInstance().broadcast(new TextComponent(formatted))); - } - } catch (Exception e) { - logger.fine("[ChatModule-Discord] Poll-Fehler für Kanal " + channelId + ": " + e.getMessage()); - } - } - - private String resolveFormat(String channelId) { - if (channelId.equals(config.getDiscordAdminChannelId())) return config.getDiscordFromFormat(); - for (net.viper.status.modules.chat.ChatChannel ch : config.getChannels().values()) { - if (channelId.equals(ch.getDiscordChannelId())) return config.getDiscordFromFormat(); - } - return null; - } - - // ===== HTTP ===== - - private void postJson(String urlStr, String payload, String authorization) throws Exception { - HttpURLConnection conn = openConnection(urlStr, "POST", authorization); - byte[] data = payload.getBytes(StandardCharsets.UTF_8); - conn.setRequestProperty("Content-Length", String.valueOf(data.length)); - conn.setDoOutput(true); - try (OutputStream os = conn.getOutputStream()) { os.write(data); } - int code = conn.getResponseCode(); - if (code >= 400) logger.warning("[ChatModule-Discord] HTTP " + code + ": " + readStream(conn.getErrorStream())); - conn.disconnect(); - } - - private String getJson(String urlStr, String authorization) throws Exception { - HttpURLConnection conn = openConnection(urlStr, "GET", authorization); - int code = conn.getResponseCode(); - if (code != 200) { conn.disconnect(); return null; } - String result = readStream(conn.getInputStream()); - conn.disconnect(); - return result; - } - - private HttpURLConnection openConnection(String urlStr, String method, String authorization) throws Exception { - HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection(); - conn.setRequestMethod(method); - conn.setConnectTimeout(5000); - conn.setReadTimeout(8000); - conn.setRequestProperty("Content-Type", "application/json"); - conn.setRequestProperty("User-Agent", "StatusAPI-ChatModule/1.0"); - if (authorization != null && !authorization.isEmpty()) conn.setRequestProperty("Authorization", authorization); - return conn; - } - - private String readStream(InputStream in) throws IOException { - if (in == null) return ""; - try (BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { - StringBuilder sb = new StringBuilder(); String line; - while ((line = br.readLine()) != null) sb.append(line); - return sb.toString(); - } - } - - // ===== JSON Mini-Parser ===== - - private static class DiscordMessage { - long id; - String authorId = "", authorName = "", content = ""; - boolean isBot = false; - } - - private java.util.List parseMessages(String json) { - java.util.List result = new java.util.ArrayList<>(); - int depth = 0, start = -1; - for (int i = 0; i < json.length(); i++) { - char c = json.charAt(i); - if (c == '{') { if (depth++ == 0) start = i; } - else if (c == '}') { - if (--depth == 0 && start != -1) { - DiscordMessage msg = parseMessage(json.substring(start, i + 1)); - if (msg != null) result.add(msg); - start = -1; - } - } - } - return result; - } - - private DiscordMessage parseMessage(String obj) { - try { - DiscordMessage msg = new DiscordMessage(); - msg.id = Long.parseLong(extractJsonString(obj, "id")); - msg.content = unescapeJson(extractJsonString(obj, "content")); - - // Webhook-Nachrichten als Bot markieren (Echo-Loop verhindern) - if (!extractJsonString(obj, "webhook_id").isEmpty()) { - msg.isBot = true; - return msg; - } - - int authStart = obj.indexOf("\"author\""); - if (authStart >= 0) { - String authBlock = extractJsonObject(obj, authStart); - msg.authorId = extractJsonString(authBlock, "id"); - msg.authorName = unescapeJson(extractJsonString(authBlock, "username")); - msg.isBot = "true".equals(extractJsonString(authBlock, "bot")); - } - return msg; - } catch (Exception e) { - return null; - } - } - - /** - * FIX #12: Escape-Sequenzen werden korrekt mit einem Escape-Flag behandelt - * statt den Vorgänger-Char zu vergleichen (der bei '\\' + '"' versagt). - * Gibt immer einen leeren String zurück wenn der Key nicht gefunden wird (nie null). - */ - private String extractJsonString(String json, String key) { - if (json == null || key == null) return ""; - String fullKey = "\"" + key + "\""; - int keyIdx = json.indexOf(fullKey); - if (keyIdx < 0) return ""; - int colon = json.indexOf(':', keyIdx + fullKey.length()); - if (colon < 0) return ""; - int valStart = colon + 1; - while (valStart < json.length() && json.charAt(valStart) == ' ') valStart++; - if (valStart >= json.length()) return ""; - char first = json.charAt(valStart); - if (first == '"') { - // FIX: Expliziter Escape-Flag statt Vorgänger-Char-Vergleich - int end = valStart + 1; - boolean escaped = false; - while (end < json.length()) { - char ch = json.charAt(end); - if (escaped) { - escaped = false; - } else if (ch == '\\') { - escaped = true; - } else if (ch == '"') { - break; - } - end++; - } - return json.substring(valStart + 1, end); - } else { - int end = valStart; - while (end < json.length() && ",}\n".indexOf(json.charAt(end)) < 0) end++; - return json.substring(valStart, end).trim(); - } - } - - private String extractJsonObject(String json, int fromIndex) { - int depth = 0, start = -1; - for (int i = fromIndex; i < json.length(); i++) { - char c = json.charAt(i); - if (c == '{') { if (depth++ == 0) start = i; } - else if (c == '}') { if (--depth == 0 && start >= 0) return json.substring(start, i + 1); } - } - return ""; - } - - private static String escapeJson(String s) { - if (s == null) return ""; - return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t"); - } - - private static String unescapeJson(String s) { - if (s == null) return ""; - return s.replace("\\\"", "\"").replace("\\n", "\n").replace("\\r", "\r").replace("\\\\", "\\"); - } -}