Upload via Git Manager GUI - BroadcastModule.java

This commit is contained in:
2026-04-01 10:15:05 +00:00
parent 6b127573bd
commit f053a8f96d

View File

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