diff --git a/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java b/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java deleted file mode 100644 index 9228034..0000000 --- a/src/main/java/net/viper/status/modules/broadcast/BroadcastModule.java +++ /dev/null @@ -1,381 +0,0 @@ -package net.viper.status.modules.broadcast; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.plugin.Listener; -import net.viper.status.module.Module; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -/** - * BroadcastModule - * - * Speichert geplante Broadcasts jetzt persistent in 'broadcasts.schedules'. - * Beim Neustart werden diese automatisch wieder geladen. - */ -public class BroadcastModule implements Module, Listener { - - private Plugin plugin; - private boolean enabled = true; - private String requiredApiKey = ""; - private String format = "%prefix% %message%"; - private String fallbackPrefix = "[Broadcast]"; - private String fallbackPrefixColor = "&c"; - private String fallbackBracketColor = "&8"; // Neu - private String fallbackMessageColor = "&f"; - - private final Map scheduledByClientId = new ConcurrentHashMap<>(); - private File schedulesFile; - private final SimpleDateFormat dateFormat; - - public BroadcastModule() { - dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - @Override - public String getName() { - return "BroadcastModule"; - } - - @Override - public void onEnable(Plugin plugin) { - this.plugin = plugin; - schedulesFile = new File(plugin.getDataFolder(), "broadcasts.schedules"); - loadConfig(); - - if (!enabled) { - plugin.getLogger().info("[BroadcastModule] deaktiviert via verify.properties (broadcast.enabled=false)"); - return; - } - - try { - plugin.getProxy().getPluginManager().registerListener(plugin, this); - } catch (Throwable ignored) {} - - plugin.getLogger().info("[BroadcastModule] aktiviert. Format: " + format); - loadSchedules(); - plugin.getProxy().getScheduler().schedule(plugin, this::processScheduled, 1, 1, TimeUnit.SECONDS); - } - - @Override - public void onDisable(Plugin plugin) { - plugin.getLogger().info("[BroadcastModule] deaktiviert."); - saveSchedules(); - scheduledByClientId.clear(); - } - - private void loadConfig() { - File file = new File(plugin.getDataFolder(), "verify.properties"); - if (!file.exists()) { - enabled = true; - requiredApiKey = ""; - format = "%prefix% %message%"; - fallbackPrefix = "[Broadcast]"; - fallbackPrefixColor = "&c"; - fallbackBracketColor = "&8"; // Neu - fallbackMessageColor = "&f"; - return; - } - - try (InputStream in = new FileInputStream(file)) { - Properties props = new Properties(); - props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - enabled = Boolean.parseBoolean(props.getProperty("broadcast.enabled", "true")); - requiredApiKey = props.getProperty("broadcast.api_key", "").trim(); - format = props.getProperty("broadcast.format", format).trim(); - if (format.isEmpty()) format = "%prefix% %message%"; - fallbackPrefix = props.getProperty("broadcast.prefix", fallbackPrefix).trim(); - fallbackPrefixColor = props.getProperty("broadcast.prefix-color", fallbackPrefixColor).trim(); - fallbackBracketColor = props.getProperty("broadcast.bracket-color", fallbackBracketColor).trim(); // Neu - fallbackMessageColor = props.getProperty("broadcast.message-color", fallbackMessageColor).trim(); - } catch (IOException e) { - plugin.getLogger().warning("[BroadcastModule] Fehler beim Laden von verify.properties: " + e.getMessage()); - } - } - - public boolean handleBroadcast(String sourceName, String message, String type, String apiKeyHeader, - String prefix, String prefixColor, String bracketColor, String messageColor) { - loadConfig(); - - if (!enabled) { - plugin.getLogger().info("[BroadcastModule] Broadcast abgelehnt: Modul ist deaktiviert."); - return false; - } - - if (requiredApiKey != null && !requiredApiKey.isEmpty()) { - if (apiKeyHeader == null || !requiredApiKey.equals(apiKeyHeader)) { - plugin.getLogger().warning("[BroadcastModule] Broadcast abgelehnt: API-Key fehlt oder inkorrekt."); - return false; - } - } - - if (message == null) message = ""; - if (sourceName == null || sourceName.isEmpty()) sourceName = "System"; - if (type == null) type = "global"; - - String usedPrefix = (prefix != null && !prefix.trim().isEmpty()) ? prefix.trim() : fallbackPrefix; - String usedPrefixColor = (prefixColor != null && !prefixColor.trim().isEmpty()) ? prefixColor.trim() : fallbackPrefixColor; - String usedBracketColor = (bracketColor != null && !bracketColor.trim().isEmpty()) ? bracketColor.trim() : fallbackBracketColor; // Neu - String usedMessageColor = (messageColor != null && !messageColor.trim().isEmpty()) ? messageColor.trim() : fallbackMessageColor; - - String prefixColorCode = normalizeColorCode(usedPrefixColor); - String bracketColorCode = normalizeColorCode(usedBracketColor); // Neu - String messageColorCode = normalizeColorCode(usedMessageColor); - - // --- KLAMMER LOGIK --- - String finalPrefix; - - // Wenn eine Klammerfarbe gesetzt ist, bauen wir den Prefix neu zusammen - // Format: [BracketColor][ [PrefixColor]Text [BracketColor]] - if (!bracketColorCode.isEmpty()) { - String textContent = usedPrefix; - // Entferne manuelle Klammern, falls der User [Broadcast] in das Textfeld geschrieben hat - if (textContent.startsWith("[")) textContent = textContent.substring(1); - if (textContent.endsWith("]")) textContent = textContent.substring(0, textContent.length() - 1); - - finalPrefix = bracketColorCode + "[" + prefixColorCode + textContent + bracketColorCode + "]" + ChatColor.RESET; - } else { - // Altes Verhalten: Ganzen String einfärben - finalPrefix = prefixColorCode + usedPrefix + ChatColor.RESET; - } - // --------------------- - - String coloredMessage = (messageColorCode.isEmpty() ? "" : messageColorCode) + message; - - String out = format.replace("%name%", sourceName) - .replace("%prefix%", finalPrefix) // Neu verwendete Variable - .replace("%prefixColored%", finalPrefix) // Fallback - .replace("%message%", message) - .replace("%messageColored%", coloredMessage) - .replace("%type%", type); - - if (!out.contains("%prefixColored%") && !out.contains("%messageColored%") && !out.contains("%prefix%") && !out.contains("%message%")) { - out = finalPrefix + " " + coloredMessage; - } - - TextComponent tc = new TextComponent(out); - int sent = 0; - for (ProxiedPlayer p : plugin.getProxy().getPlayers()) { - try { p.sendMessage(tc); sent++; } catch (Throwable ignored) {} - } - - plugin.getLogger().info("[BroadcastModule] Broadcast gesendet (Empfänger=" + sent + "): " + message); - return true; - } - - private String normalizeColorCode(String code) { - if (code == null) return ""; - code = code.trim(); - if (code.isEmpty()) return ""; - return code.contains("&") ? ChatColor.translateAlternateColorCodes('&', code) : code; - } - - private void saveSchedules() { - Properties props = new Properties(); - for (Map.Entry entry : scheduledByClientId.entrySet()) { - String id = entry.getKey(); - ScheduledBroadcast sb = entry.getValue(); - props.setProperty(id + ".nextRunMillis", String.valueOf(sb.nextRunMillis)); - props.setProperty(id + ".sourceName", sb.sourceName); - props.setProperty(id + ".message", sb.message); - props.setProperty(id + ".type", sb.type); - props.setProperty(id + ".prefix", sb.prefix); - props.setProperty(id + ".prefixColor", sb.prefixColor); - props.setProperty(id + ".bracketColor", sb.bracketColor); // Neu - props.setProperty(id + ".messageColor", sb.messageColor); - props.setProperty(id + ".recur", sb.recur); - } - - try (OutputStream out = new FileOutputStream(schedulesFile)) { - props.store(out, "PulseCast Scheduled Broadcasts"); - } catch (IOException e) { - plugin.getLogger().severe("[BroadcastModule] Konnte Schedules nicht speichern: " + e.getMessage()); - } - } - - private void loadSchedules() { - if (!schedulesFile.exists()) { - plugin.getLogger().info("[BroadcastModule] Keine bestehenden Schedules gefunden (Neustart)."); - return; - } - - Properties props = new Properties(); - try (InputStream in = new FileInputStream(schedulesFile)) { - props.load(in); - } catch (IOException e) { - plugin.getLogger().severe("[BroadcastModule] Konnte Schedules nicht laden: " + e.getMessage()); - return; - } - - Map loaded = new HashMap<>(); - for (String key : props.stringPropertyNames()) { - if (!key.contains(".")) continue; - String[] parts = key.split("\\."); - if (parts.length != 2) continue; - - String id = parts[0]; - String field = parts[1]; - String value = props.getProperty(key); - - ScheduledBroadcast sb = loaded.get(id); - if (sb == null) { - sb = new ScheduledBroadcast(id, 0, "", "", "", "", "", "", "", ""); // Ein leerer String mehr für Bracket - loaded.put(id, sb); - } - - switch (field) { - case "nextRunMillis": - try { sb.nextRunMillis = Long.parseLong(value); } catch (NumberFormatException ignored) {} - break; - case "sourceName": sb.sourceName = value; break; - case "message": sb.message = value; break; - case "type": sb.type = value; break; - case "prefix": sb.prefix = value; break; - case "prefixColor": sb.prefixColor = value; break; - case "bracketColor": sb.bracketColor = value; break; // Neu - case "messageColor": sb.messageColor = value; break; - case "recur": sb.recur = value; break; - } - } - scheduledByClientId.putAll(loaded); - plugin.getLogger().info("[BroadcastModule] " + loaded.size() + " geplante Broadcasts aus Datei wiederhergestellt."); - } - - public boolean scheduleBroadcast(long timestampMillis, String sourceName, String message, String type, - String apiKeyHeader, String prefix, String prefixColor, String bracketColor, String messageColor, - String recur, String clientScheduleId) { - loadConfig(); - - if (!enabled) { - plugin.getLogger().info("[BroadcastModule] schedule abgelehnt: Modul deaktiviert."); - return false; - } - - if (requiredApiKey != null && !requiredApiKey.isEmpty()) { - if (apiKeyHeader == null || !requiredApiKey.equals(apiKeyHeader)) { - plugin.getLogger().warning("[BroadcastModule] schedule abgelehnt: API-Key fehlt oder inkorrekt."); - return false; - } - } - - if (message == null) message = ""; - if (sourceName == null || sourceName.isEmpty()) sourceName = "System"; - if (type == null) type = "global"; - if (recur == null) recur = "none"; - - String id = (clientScheduleId != null && !clientScheduleId.trim().isEmpty()) ? clientScheduleId.trim() : UUID.randomUUID().toString(); - - long now = System.currentTimeMillis(); - String scheduledTimeStr = dateFormat.format(new Date(timestampMillis)); - - plugin.getLogger().info("[BroadcastModule] Neue geplante Nachricht registriert: " + id + " @ " + scheduledTimeStr); - - if (timestampMillis <= now) { - plugin.getLogger().warning("[BroadcastModule] Geplante Zeit liegt in der Vergangenheit -> sende sofort!"); - return handleBroadcast(sourceName, message, type, apiKeyHeader, prefix, prefixColor, bracketColor, messageColor); - } - - ScheduledBroadcast sb = new ScheduledBroadcast(id, timestampMillis, sourceName, message, type, prefix, prefixColor, bracketColor, messageColor, recur); - scheduledByClientId.put(id, sb); - saveSchedules(); - return true; - } - - public boolean cancelScheduled(String clientScheduleId) { - if (clientScheduleId == null || clientScheduleId.trim().isEmpty()) return false; - ScheduledBroadcast removed = scheduledByClientId.remove(clientScheduleId); - if (removed != null) { - plugin.getLogger().info("[BroadcastModule] Geplante Nachricht abgebrochen: id=" + clientScheduleId); - saveSchedules(); - return true; - } - return false; - } - - private void processScheduled() { - if (scheduledByClientId.isEmpty()) return; - - long now = System.currentTimeMillis(); - List toRemove = new ArrayList<>(); - boolean changed = false; - - for (Map.Entry entry : scheduledByClientId.entrySet()) { - ScheduledBroadcast sb = entry.getValue(); - - if (sb.nextRunMillis <= now) { - String timeStr = dateFormat.format(new Date(sb.nextRunMillis)); - plugin.getLogger().info("[BroadcastModule] ⏰ Sende geplante Nachricht (ID: " + entry.getKey() + ", Zeit: " + timeStr + ")"); - - handleBroadcast(sb.sourceName, sb.message, sb.type, "", sb.prefix, sb.prefixColor, sb.bracketColor, sb.messageColor); - - if (!"none".equalsIgnoreCase(sb.recur)) { - long next = computeNextMillis(sb.nextRunMillis, sb.recur); - if (next > 0) { - sb.nextRunMillis = next; - String nextTimeStr = dateFormat.format(new Date(next)); - plugin.getLogger().info("[BroadcastModule] Nächste Wiederholung (" + sb.recur + "): " + nextTimeStr); - changed = true; - } else { - toRemove.add(entry.getKey()); - changed = true; - } - } else { - toRemove.add(entry.getKey()); - changed = true; - } - } - } - - if (changed || !toRemove.isEmpty()) { - for (String k : toRemove) { - scheduledByClientId.remove(k); - plugin.getLogger().info("[BroadcastModule] Schedule entfernt: " + k); - } - saveSchedules(); - } - } - - private long computeNextMillis(long currentMillis, String recur) { - switch (recur.toLowerCase(Locale.ROOT)) { - case "hourly": return currentMillis + TimeUnit.HOURS.toMillis(1); - case "daily": return currentMillis + TimeUnit.DAYS.toMillis(1); - case "weekly": return currentMillis + TimeUnit.DAYS.toMillis(7); - default: return -1L; - } - } - - private static class ScheduledBroadcast { - final String clientId; - long nextRunMillis; - String sourceName; - String message; - String type; - String prefix; - String prefixColor; - String bracketColor; // Neu - String messageColor; - String recur; - - ScheduledBroadcast(String clientId, long nextRunMillis, String sourceName, String message, String type, - String prefix, String prefixColor, String bracketColor, String messageColor, String recur) { - this.clientId = clientId; - this.nextRunMillis = nextRunMillis; - this.sourceName = sourceName; - this.message = message; - this.type = type; - this.prefix = prefix; - this.prefixColor = prefixColor; - this.bracketColor = bracketColor; // Neu - this.messageColor = messageColor; - this.recur = (recur == null ? "none" : recur); - } - } -} \ No newline at end of file