diff --git a/src/main/java/net/viper/status/modules/chat/AccountLinkManager.java b/src/main/java/net/viper/status/modules/chat/AccountLinkManager.java deleted file mode 100644 index 0260d62..0000000 --- a/src/main/java/net/viper/status/modules/chat/AccountLinkManager.java +++ /dev/null @@ -1,318 +0,0 @@ -package net.viper.status.modules.chat; - -import java.io.*; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -/** - * Verwaltet die Verknüpfung von Minecraft-Accounts mit Discord/Telegram. - * - * Ablauf: - * 1. Spieler tippt /linkdiscord oder /linktelegram → Token wird generiert - * 2. Spieler schickt Token an den Bot - * 3. Bot-Polling erkennt Token → Verknüpfung wird gespeichert - * - * Speicherformat (chat_links.dat): - * minecraft:|name:|discord:|telegram: - */ -public class AccountLinkManager { - - private final File file; - private final Logger logger; - - // UUID → verknüpfte Accounts - private final ConcurrentHashMap links = new ConcurrentHashMap<>(); - - // Ausstehende Token: token → UUID (läuft nach 10 Min ab) - private final ConcurrentHashMap pendingTokens = new ConcurrentHashMap<>(); - - public AccountLinkManager(File dataFolder, Logger logger) { - this.file = new File(dataFolder, "chat_links.dat"); - this.logger = logger; - } - - // ===== Datenklassen ===== - - public static class LinkedAccount { - public UUID minecraftUUID; - public String minecraftName; - public String discordUserId = ""; // leer = nicht verknüpft - public String telegramUserId = ""; // leer = nicht verknüpft - public String telegramUsername = ""; // @username für Anzeige - public String discordUsername = ""; // für Anzeige - } - - private static class PendingToken { - UUID uuid; - String playerName; - String type; // "discord" oder "telegram" - long expiresAt; // Unix-Millis - - PendingToken(UUID uuid, String playerName, String type) { - this.uuid = uuid; - this.playerName = playerName; - this.type = type; - this.expiresAt = System.currentTimeMillis() + (10 * 60 * 1000L); // 10 Min - } - - boolean isExpired() { - return System.currentTimeMillis() > expiresAt; - } - } - - // ===== Token-Generierung ===== - - /** - * Generiert einen neuen Verknüpfungs-Token für einen Spieler. - * Bestehende Token für denselben Spieler+Typ werden überschrieben. - * - * @param uuid UUID des Spielers - * @param playerName Anzeigename - * @param type "discord" oder "telegram" - * @return 6-stelliger alphanumerischer Token (z.B. "A3F9K2") - */ - public String generateToken(UUID uuid, String playerName, String type) { - // Alte Token für diesen Spieler+Typ entfernen - pendingTokens.entrySet().removeIf(e -> - e.getValue().uuid.equals(uuid) && e.getValue().type.equals(type)); - - // Abgelaufene Token bereinigen - pendingTokens.entrySet().removeIf(e -> e.getValue().isExpired()); - - String token; - do { - token = generateRandomToken(); - } while (pendingTokens.containsKey(token)); - - pendingTokens.put(token, new PendingToken(uuid, playerName, type)); - return token; - } - - private String generateRandomToken() { - String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // ohne I,O,0,1 (Verwechslungsgefahr) - Random rnd = new Random(); - StringBuilder sb = new StringBuilder(6); - for (int i = 0; i < 6; i++) sb.append(chars.charAt(rnd.nextInt(chars.length()))); - return sb.toString(); - } - - // ===== Token einlösen ===== - - /** - * Versucht einen Token einzulösen (aufgerufen wenn Bot eine Nachricht empfängt). - * - * @param token Der eingesendete Token - * @param externalId Discord User-ID oder Telegram User-ID (als String) - * @param externalName Discord-Username oder Telegram-@username - * @return LinkedAccount wenn erfolgreich, null wenn Token ungültig/abgelaufen - */ - public LinkedAccount redeemToken(String token, String externalId, String externalName, String type) { - token = token.trim().toUpperCase(); - PendingToken pending = pendingTokens.get(token); - - if (pending == null || pending.isExpired()) { - pendingTokens.remove(token); - return null; - } - // Typ muss übereinstimmen - if (!pending.type.equals(type)) return null; - - pendingTokens.remove(token); - - // Bestehenden Account holen oder neu anlegen - LinkedAccount account = links.computeIfAbsent(pending.uuid, k -> { - LinkedAccount a = new LinkedAccount(); - a.minecraftUUID = pending.uuid; - a.minecraftName = pending.playerName; - return a; - }); - account.minecraftName = pending.playerName; // aktuell halten - - if ("discord".equals(pending.type)) { - account.discordUserId = externalId; - account.discordUsername = externalName; - } else if ("telegram".equals(pending.type)) { - account.telegramUserId = externalId; - account.telegramUsername = externalName; - } - - save(); - return account; - } - - // ===== Lookup ===== - - public LinkedAccount getByUUID(UUID uuid) { - return links.get(uuid); - } - - public LinkedAccount getByDiscordId(String discordUserId) { - for (LinkedAccount a : links.values()) { - if (discordUserId.equals(a.discordUserId)) return a; - } - return null; - } - - public LinkedAccount getByTelegramId(String telegramUserId) { - for (LinkedAccount a : links.values()) { - if (telegramUserId.equals(a.telegramUserId)) return a; - } - return null; - } - - /** Gibt den Minecraft-Namen für eine Discord-User-ID zurück, oder null. */ - public String getMinecraftNameByDiscordId(String discordUserId) { - LinkedAccount a = getByDiscordId(discordUserId); - return a != null ? a.minecraftName : null; - } - - /** Gibt den Minecraft-Namen für eine Telegram-User-ID zurück, oder null. */ - public String getMinecraftNameByTelegramId(String telegramUserId) { - LinkedAccount a = getByTelegramId(telegramUserId); - return a != null ? a.minecraftName : null; - } - - /** Prüft ob ein Token gerade aussteht (für Tab-Complete etc.). */ - public boolean hasPendingToken(UUID uuid, String type) { - for (PendingToken t : pendingTokens.values()) { - if (t.uuid.equals(uuid) && t.type.equals(type) && !t.isExpired()) return true; - } - return false; - } - - // ===== Verknüpfung aufheben ===== - - public boolean unlinkDiscord(UUID uuid) { - LinkedAccount a = links.get(uuid); - if (a == null || a.discordUserId.isEmpty()) return false; - a.discordUserId = ""; - a.discordUsername = ""; - cleanupEmpty(uuid); - save(); - return true; - } - - public boolean unlinkTelegram(UUID uuid) { - LinkedAccount a = links.get(uuid); - if (a == null || a.telegramUserId.isEmpty()) return false; - a.telegramUserId = ""; - a.telegramUsername = ""; - cleanupEmpty(uuid); - save(); - return true; - } - - private void cleanupEmpty(UUID uuid) { - LinkedAccount a = links.get(uuid); - if (a != null && a.discordUserId.isEmpty() && a.telegramUserId.isEmpty()) { - links.remove(uuid); - } - } - - // ===== Convenience-Methoden für Bridges ===== - - /** - * Löst einen Telegram-Token ein. - * Wrapper für redeemToken mit type="telegram". - */ - public LinkedAccount redeemTelegram(String token, String telegramUserId, String telegramUsername) { - return redeemToken(token, telegramUserId, telegramUsername, "telegram"); - } - - /** - * Löst einen Discord-Token ein. - * Wrapper für redeemToken mit type="discord". - */ - public LinkedAccount redeemDiscord(String token, String discordUserId, String discordUsername) { - return redeemToken(token, discordUserId, discordUsername, "discord"); - } - - /** - * Gibt den Anzeigenamen für einen Telegram-Nutzer zurück. - * Wenn verknüpft: "MinecraftName (@telegram)", sonst: "@telegram" - */ - public String resolveTelegramName(String telegramUserId, String fallbackName) { - String mc = getMinecraftNameByTelegramId(telegramUserId); - return mc != null ? mc : fallbackName; - } - - /** - * Gibt den Anzeigenamen für einen Discord-Nutzer zurück. - * Wenn verknüpft: Minecraft-Name, sonst: Discord-Username - */ - public String resolveDiscordName(String discordUserId, String fallbackName) { - String mc = getMinecraftNameByDiscordId(discordUserId); - return mc != null ? mc : fallbackName; - } - - /** - * Gibt das korrekte Format-String zurück abhängig ob Account verknüpft. - * linked=true → linkedFormat (mit {player}), false → unlinkedFormat (mit {user}) - */ - public boolean isLinkedTelegram(String telegramUserId) { - return getByTelegramId(telegramUserId) != null; - } - - public boolean isLinkedDiscord(String discordUserId) { - return getByDiscordId(discordUserId) != null; - } - - // ===== Persistenz ===== - - public void save() { - try (BufferedWriter bw = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) { - for (LinkedAccount a : links.values()) { - bw.write(a.minecraftUUID - + "|" + esc(a.minecraftName) - + "|" + esc(a.discordUserId) - + "|" + esc(a.discordUsername) - + "|" + esc(a.telegramUserId) - + "|" + esc(a.telegramUsername)); - bw.newLine(); - } - } catch (IOException e) { - logger.warning("[ChatModule] Fehler beim Speichern der Account-Links: " + e.getMessage()); - } - } - - public void load() { - links.clear(); - if (!file.exists()) return; - try (BufferedReader br = new BufferedReader( - new InputStreamReader(new FileInputStream(file), "UTF-8"))) { - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (line.isEmpty()) continue; - String[] p = line.split("\\|", -1); - if (p.length < 6) continue; - try { - LinkedAccount a = new LinkedAccount(); - a.minecraftUUID = UUID.fromString(p[0]); - a.minecraftName = unesc(p[1]); - a.discordUserId = unesc(p[2]); - a.discordUsername = unesc(p[3]); - a.telegramUserId = unesc(p[4]); - a.telegramUsername = unesc(p[5]); - if (!a.discordUserId.isEmpty() || !a.telegramUserId.isEmpty()) { - links.put(a.minecraftUUID, a); - } - } catch (Exception ignored) {} - } - } catch (IOException e) { - logger.warning("[ChatModule] Fehler beim Laden der Account-Links: " + e.getMessage()); - } - } - - private static String esc(String s) { - if (s == null) return ""; - return s.replace("\\", "\\\\").replace("|", "\\p"); - } - - private static String unesc(String s) { - if (s == null) return ""; - return s.replace("\\p", "|").replace("\\\\", "\\"); - } -} \ No newline at end of file