src/main/java/net/viper/status/StatusAPI.java aktualisiert
This commit is contained in:
@@ -10,71 +10,68 @@ import net.viper.status.stats.StatsModule;
|
|||||||
import net.viper.status.modules.verify.VerifyModule;
|
import net.viper.status.modules.verify.VerifyModule;
|
||||||
import net.viper.status.modules.globalchat.GlobalChatModule;
|
import net.viper.status.modules.globalchat.GlobalChatModule;
|
||||||
import net.viper.status.modules.navigation.NavigationModule;
|
import net.viper.status.modules.navigation.NavigationModule;
|
||||||
|
import net.viper.status.modules.commandblocker.CommandBlockerModule;
|
||||||
|
import net.viper.status.modules.broadcast.BroadcastModule;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StatusAPI - zentraler Bungee HTTP-Status- und Broadcast-Endpunkt
|
* StatusAPI - zentraler Bungee HTTP-Status- und Broadcast-Endpunkt
|
||||||
*
|
|
||||||
* Ergänzungen:
|
|
||||||
* - BroadcastModule Registrierung
|
|
||||||
* - POST /broadcast UND POST / (Root) werden als Broadcasts behandelt
|
|
||||||
* - Unterstützung für prefix, prefixColor, messageColor Felder in JSON-Payload
|
|
||||||
* - Unterstützung für bracketColor (neu)
|
|
||||||
* - Unterstützung für scheduleTime (ms oder s), recur, clientScheduleId
|
|
||||||
* - API-Key Header (X-Api-Key) wird an BroadcastModule weitergereicht
|
|
||||||
*/
|
*/
|
||||||
public class StatusAPI extends Plugin implements Runnable {
|
public class StatusAPI extends Plugin implements Runnable {
|
||||||
|
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private int port = 9191;
|
private int port = 9191;
|
||||||
|
|
||||||
// Das neue Modul-System
|
|
||||||
private ModuleManager moduleManager;
|
private ModuleManager moduleManager;
|
||||||
|
|
||||||
// Alte Komponenten (UpdateChecker/FileDownloader bleiben hier, da sie Core-Updates steuern)
|
|
||||||
private UpdateChecker updateChecker;
|
private UpdateChecker updateChecker;
|
||||||
private FileDownloader fileDownloader;
|
private FileDownloader fileDownloader;
|
||||||
|
private Properties verifyProperties;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
getLogger().info("StatusAPI Core wird initialisiert...");
|
getLogger().info("StatusAPI Core wird initialisiert...");
|
||||||
|
|
||||||
// 1. Ordner sicherstellen
|
|
||||||
if (!getDataFolder().exists()) {
|
if (!getDataFolder().exists()) {
|
||||||
getDataFolder().mkdirs();
|
getDataFolder().mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Modul-System starten
|
// Config mergen/updaten
|
||||||
|
mergeVerifyConfig();
|
||||||
|
|
||||||
moduleManager = new ModuleManager();
|
moduleManager = new ModuleManager();
|
||||||
|
|
||||||
// 3. MODULE REGISTRIEREN
|
// Module registrieren
|
||||||
moduleManager.registerModule(new StatsModule()); // Statistik System laden
|
moduleManager.registerModule(new StatsModule());
|
||||||
moduleManager.registerModule(new VerifyModule()); // Verify Modul
|
moduleManager.registerModule(new VerifyModule());
|
||||||
moduleManager.registerModule(new GlobalChatModule()); // GlobalChat
|
moduleManager.registerModule(new GlobalChatModule());
|
||||||
moduleManager.registerModule(new NavigationModule()); //Server Switcher
|
moduleManager.registerModule(new NavigationModule());
|
||||||
// Broadcast Modul registrieren (neu)
|
moduleManager.registerModule(new BroadcastModule());
|
||||||
moduleManager.registerModule(new net.viper.status.modules.broadcast.BroadcastModule());
|
moduleManager.registerModule(new CommandBlockerModule());
|
||||||
|
|
||||||
// 4. Alle Module aktivieren
|
|
||||||
moduleManager.enableAll(this);
|
moduleManager.enableAll(this);
|
||||||
|
|
||||||
// 5. WebServer Thread starten
|
// WebServer starten
|
||||||
getLogger().info("Starte Web-Server auf Port " + port + "...");
|
getLogger().info("Starte Web-Server auf Port " + port + "...");
|
||||||
thread = new Thread(this, "StatusAPI-HTTP-Server");
|
thread = new Thread(this, "StatusAPI-HTTP-Server");
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
// 6. Update System Initialisieren
|
// Update System
|
||||||
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
|
String currentVersion = getDescription() != null ? getDescription().getVersion() : "0.0.0";
|
||||||
updateChecker = new UpdateChecker(this, currentVersion, 6);
|
updateChecker = new UpdateChecker(this, currentVersion, 6);
|
||||||
fileDownloader = new FileDownloader(this);
|
fileDownloader = new FileDownloader(this);
|
||||||
@@ -82,16 +79,13 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
File pluginFile = getFile();
|
File pluginFile = getFile();
|
||||||
File backupFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.bak");
|
File backupFile = new File(pluginFile.getParentFile(), "StatusAPI.jar.bak");
|
||||||
|
|
||||||
// Backup Cleanup
|
|
||||||
if (backupFile.exists()) {
|
if (backupFile.exists()) {
|
||||||
ProxyServer.getInstance().getScheduler().schedule(this, () -> {
|
ProxyServer.getInstance().getScheduler().schedule(this, () -> {
|
||||||
if (backupFile.exists()) backupFile.delete();
|
if (backupFile.exists()) backupFile.delete();
|
||||||
},1, TimeUnit.MINUTES);
|
},1, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sofortiger Check
|
|
||||||
checkAndMaybeUpdate();
|
checkAndMaybeUpdate();
|
||||||
// Regelmäßiger Check
|
|
||||||
ProxyServer.getInstance().getScheduler().schedule(this, this::checkAndMaybeUpdate, 6, 6, TimeUnit.HOURS);
|
ProxyServer.getInstance().getScheduler().schedule(this, this::checkAndMaybeUpdate, 6, 6, TimeUnit.HOURS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +103,225 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Update Logik (unverändert) ---
|
/**
|
||||||
|
* Hilfsmethode um Property-Werte zu bereinigen (unescape).
|
||||||
|
* Entfernt z.B. den Backslash vor URLs wie https\://
|
||||||
|
*/
|
||||||
|
private String unescapePropertiesString(String input) {
|
||||||
|
if (input == null) return null;
|
||||||
|
try {
|
||||||
|
Properties p = new Properties();
|
||||||
|
p.load(new StringReader("dummy=" + input));
|
||||||
|
return p.getProperty("dummy");
|
||||||
|
} catch (IOException e) {
|
||||||
|
return input; // Fallback, falls Parsing fehlschlägt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MERGE LOGIK ---
|
||||||
|
private void mergeVerifyConfig() {
|
||||||
|
try {
|
||||||
|
File file = new File(getDataFolder(), "verify.properties");
|
||||||
|
|
||||||
|
// 1. Das exakte Template definieren
|
||||||
|
List<String> templateLines = Arrays.asList(
|
||||||
|
"# _____ __ __ ___ ____ ____",
|
||||||
|
"# / ___// /_____ _/ /___ _______/ | / __ \\/ _/",
|
||||||
|
"# \\__ \\/ __/ __ `/ __/ / / / ___/ /| | / /_/ // / ",
|
||||||
|
"# ___/ / /_/ /_/ / /_/ /_/ (__ ) ___ |/ ____// / ",
|
||||||
|
"# /____/\\__/\\__,_/\\__/\\__,_/____/_/ |_/_/ /___/ ",
|
||||||
|
" ",
|
||||||
|
"# ===========================",
|
||||||
|
"# GLOBALCHAT AKTIVIERUNG",
|
||||||
|
"# ===========================",
|
||||||
|
"chat.enabled=false",
|
||||||
|
"",
|
||||||
|
"# ------------------------------",
|
||||||
|
"# Broadcast",
|
||||||
|
"# ------------------------------",
|
||||||
|
"",
|
||||||
|
"broadcast.enabled=false",
|
||||||
|
"broadcast.prefix=[Broadcast]",
|
||||||
|
"broadcast.prefix-color=&c",
|
||||||
|
"broadcast.message-color=&f",
|
||||||
|
"broadcast.format=%prefixColored% %messageColored%",
|
||||||
|
"# broadcast.format kann angepasst werden; nutze Platzhalter: %name%, %prefix%, %prefixColored%, %message%, %messageColored%, %type%",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# NAVIGATION / SERVER SWITCHER",
|
||||||
|
"# ===========================",
|
||||||
|
"# Hier kannst du das interne Navigationssystem aktivieren/deaktivieren.",
|
||||||
|
"# Wenn aktiviert, erstellt das Plugin automatisch Befehle basierend auf den Servernamen (z.B. /lobby, /survival).",
|
||||||
|
"navigation.enabled=false",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# WORDPRESS / VERIFY EINSTELLUNGEN",
|
||||||
|
"# ===========================",
|
||||||
|
"wp_verify_url=https://deine-wp-domain.tld",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# SERVER KONFIGURATION",
|
||||||
|
"# ===========================",
|
||||||
|
"# Hier legst du für jeden Server alles fest:",
|
||||||
|
"# 1. Den Anzeigenamen für den Chat (z.B. &bLobby)",
|
||||||
|
"# 2. Die Server ID für WordPress (z.B. id=1)",
|
||||||
|
"# 3. Das Secret für WordPress (z.B. secret=...)",
|
||||||
|
"",
|
||||||
|
"# Server 1: Lobby",
|
||||||
|
"server.lobby=&bLobby",
|
||||||
|
"server.lobby.id=1",
|
||||||
|
"server.lobby.secret=GeheimesWortFuerLobby123",
|
||||||
|
"",
|
||||||
|
"# Server 2: Survival",
|
||||||
|
"server.survival=&aSurvival",
|
||||||
|
"server.survival.id=2",
|
||||||
|
"server.survival.secret=GeheimesWortFuerSurvival456",
|
||||||
|
"",
|
||||||
|
"# Server 3: SkyBlock",
|
||||||
|
"server.skyblock=&dSkyBlock",
|
||||||
|
"server.skyblock.id=3",
|
||||||
|
"server.skyblock.secret=GeheimesWortFuerSkyBlock789",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# Manuelle Ränge (Overrides)",
|
||||||
|
"# ===========================",
|
||||||
|
"# Syntax: override.<Spieler-UUID> = <Gruppenname>",
|
||||||
|
"# WICHTIG: Die Gruppe (z.B. Owner) muss unten bei groupformat definiert sein!",
|
||||||
|
"",
|
||||||
|
"# Beispiel: Deinen UUID hier einfügen",
|
||||||
|
"override.uuid-hier-einfügen = Owner",
|
||||||
|
"override.uuid-hier-einfügen-2 = Admin",
|
||||||
|
"override.uuid-hier-einfügen-3 = Developer",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# Chat-Formate für Gruppen",
|
||||||
|
"# ===========================",
|
||||||
|
"",
|
||||||
|
"# Der Name hinter dem Punkt (z.B. Owner) muss exakt mit der LuckPerms Gruppe übereinstimmen.",
|
||||||
|
"# Nutze & für Farbcodes.",
|
||||||
|
"",
|
||||||
|
"# Ränge mit neuer Syntax: Rank || Spielerfarbe || Chatfarbe",
|
||||||
|
"# Beispiel: Rot (Rang) || Blau (Name) || Lila (Chat)",
|
||||||
|
"groupformat.owner=&c[Owner] || &b || &d",
|
||||||
|
"groupformat.admin=&4[Admin] || &9 || &c",
|
||||||
|
"groupformat.developer=&b[Dev] || &3 || &a",
|
||||||
|
"groupformat.premium=&6[Premium] || &e || &7",
|
||||||
|
"groupformat.spieler=&f[Spieler] || &7 || &8",
|
||||||
|
"",
|
||||||
|
"# ===========================",
|
||||||
|
"# COMMAND BLOCKER",
|
||||||
|
"# ===========================",
|
||||||
|
"commandblocker.enabled=true",
|
||||||
|
"commandblocker.bypass.permission=commandblocker.bypass"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Existierende Werte einlesen
|
||||||
|
Map<String, String> existingValues = new HashMap<>();
|
||||||
|
// Liste für Override-Keys, die nicht im Template stehen
|
||||||
|
List<Map.Entry<String, String>> pendingOverrides = new ArrayList<>();
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
List<String> existing = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||||
|
for (String line : existing) {
|
||||||
|
String trimmed = line.trim();
|
||||||
|
if (!trimmed.isEmpty() && !trimmed.startsWith("#") && trimmed.contains("=")) {
|
||||||
|
int idx = trimmed.indexOf('=');
|
||||||
|
String key = trimmed.substring(0, idx).trim();
|
||||||
|
String val = trimmed.substring(idx + 1).trim();
|
||||||
|
// Wert speichern, Escaping entfernen für sauberen Output
|
||||||
|
existingValues.put(key, unescapePropertiesString(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Alle Override-Keys aus der alten Config sammeln
|
||||||
|
for (Map.Entry<String, String> entry : existingValues.entrySet()) {
|
||||||
|
if (entry.getKey().startsWith("override.")) {
|
||||||
|
pendingOverrides.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Datei Zeile für Zeile basierend auf Template schreiben
|
||||||
|
List<String> outputLines = new ArrayList<>();
|
||||||
|
Set<String> usedKeys = new HashSet<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < templateLines.size(); i++) {
|
||||||
|
String line = templateLines.get(i);
|
||||||
|
String trimmed = line.trim();
|
||||||
|
|
||||||
|
// PRÜFUNG: Wenn wir am Anfang des nächsten Abschnitts sind ("Chat-Formate")
|
||||||
|
// Fügen wir die fehlenden Overrides VORHER ein.
|
||||||
|
if (trimmed.startsWith("#") && trimmed.contains("====")) {
|
||||||
|
if (i + 1 < templateLines.size()) {
|
||||||
|
String nextTrimmed = templateLines.get(i + 1).trim();
|
||||||
|
// Wenn wir kurz vor "Chat-Formate" stehen und noch Overrides offen sind -> Einfügen
|
||||||
|
if (nextTrimmed.contains("Chat-Formate") && !pendingOverrides.isEmpty()) {
|
||||||
|
outputLines.add("");
|
||||||
|
for (Map.Entry<String, String> ov : pendingOverrides) {
|
||||||
|
if (!usedKeys.contains(ov.getKey())) {
|
||||||
|
// Bereinigten Wert schreiben
|
||||||
|
outputLines.add(ov.getKey() + "=" + ov.getValue());
|
||||||
|
usedKeys.add(ov.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pendingOverrides.clear();
|
||||||
|
outputLines.add("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LOGIK: Template Zeile verarbeiten
|
||||||
|
if (!trimmed.startsWith("#") && !trimmed.isEmpty() && trimmed.contains("=")) {
|
||||||
|
String[] parts = trimmed.split("=", 2);
|
||||||
|
String key = parts[0].trim();
|
||||||
|
|
||||||
|
if (existingValues.containsKey(key)) {
|
||||||
|
// Wert aus alter Config übernehmen (bereinigt)
|
||||||
|
String cleanVal = unescapePropertiesString(existingValues.get(key));
|
||||||
|
outputLines.add(key + "=" + cleanVal);
|
||||||
|
usedKeys.add(key);
|
||||||
|
|
||||||
|
pendingOverrides.removeIf(e -> e.getKey().equals(key));
|
||||||
|
} else {
|
||||||
|
// Standardwert aus Template übernehmen
|
||||||
|
outputLines.add(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outputLines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Falls noch was übrig ist, am Ende anhängen
|
||||||
|
if (!pendingOverrides.isEmpty()) {
|
||||||
|
outputLines.add("");
|
||||||
|
for (Map.Entry<String, String> ov : pendingOverrides) {
|
||||||
|
outputLines.add(ov.getKey() + "=" + ov.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Datei schreiben
|
||||||
|
Files.write(file.toPath(), outputLines, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// 6. Properties laden
|
||||||
|
verifyProperties = new Properties();
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
verifyProperties.load(fis);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogger().info("verify.properties erfolgreich aktualisiert.");
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
getLogger().severe("Fehler beim Merge der verify.properties: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Properties getVerifyProperties() {
|
||||||
|
synchronized (this) {
|
||||||
|
return verifyProperties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Update Logik ---
|
||||||
private void checkAndMaybeUpdate() {
|
private void checkAndMaybeUpdate() {
|
||||||
try {
|
try {
|
||||||
updateChecker.checkNow();
|
updateChecker.checkNow();
|
||||||
@@ -173,7 +385,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- WebServer & JSON ---
|
// --- WebServer & JSON ---
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try (ServerSocket serverSocket = new ServerSocket(port)) {
|
try (ServerSocket serverSocket = new ServerSocket(port)) {
|
||||||
@@ -192,15 +403,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Erweiterter HTTP-Handler:
|
|
||||||
* - GET weiterhin liefert Status JSON wie bisher
|
|
||||||
* - POST an /broadcast oder POST an / (Root) wird als Broadcast verarbeitet
|
|
||||||
* (Payload JSON mit message, type, prefix, prefixColor, bracketColor, messageColor)
|
|
||||||
* - Wenn Feld scheduleTime (ms oder s) vorhanden ist, wird Nachricht als geplante Nachricht registriert
|
|
||||||
* und an BroadcastModule.scheduleBroadcast weitergegeben.
|
|
||||||
* - POST /broadcast/cancel erwartet clientScheduleId im Body und ruft cancelScheduled(clientScheduleId) auf.
|
|
||||||
*/
|
|
||||||
private void handleConnection(Socket clientSocket) {
|
private void handleConnection(Socket clientSocket) {
|
||||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
|
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
|
||||||
OutputStream out = clientSocket.getOutputStream()) {
|
OutputStream out = clientSocket.getOutputStream()) {
|
||||||
@@ -208,13 +410,11 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
String inputLine = in.readLine();
|
String inputLine = in.readLine();
|
||||||
if (inputLine == null) return;
|
if (inputLine == null) return;
|
||||||
|
|
||||||
// Request-Line zerlegen: METHOD PATH HTTP/VERSION
|
|
||||||
String[] reqParts = inputLine.split(" ");
|
String[] reqParts = inputLine.split(" ");
|
||||||
if (reqParts.length < 2) return;
|
if (reqParts.length < 2) return;
|
||||||
String method = reqParts[0].trim();
|
String method = reqParts[0].trim();
|
||||||
String path = reqParts[1].trim();
|
String path = reqParts[1].trim();
|
||||||
|
|
||||||
// Header einlesen (case-insensitive keys)
|
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
String line;
|
String line;
|
||||||
while ((line = in.readLine()) != null && !line.isEmpty()) {
|
while ((line = in.readLine()) != null && !line.isEmpty()) {
|
||||||
@@ -226,7 +426,7 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- POST /broadcast/cancel (optional) ---
|
// --- POST /broadcast/cancel ---
|
||||||
if ("POST".equalsIgnoreCase(method) && (path.equalsIgnoreCase("/broadcast/cancel") || path.equalsIgnoreCase("/cancel"))) {
|
if ("POST".equalsIgnoreCase(method) && (path.equalsIgnoreCase("/broadcast/cancel") || path.equalsIgnoreCase("/cancel"))) {
|
||||||
int contentLength = 0;
|
int contentLength = 0;
|
||||||
if (headers.containsKey("content-length")) {
|
if (headers.containsKey("content-length")) {
|
||||||
@@ -248,8 +448,8 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object mod = moduleManager.getModule("BroadcastModule");
|
Object mod = moduleManager.getModule("BroadcastModule");
|
||||||
if (mod instanceof net.viper.status.modules.broadcast.BroadcastModule) {
|
if (mod instanceof BroadcastModule) {
|
||||||
boolean ok = ((net.viper.status.modules.broadcast.BroadcastModule) mod).cancelScheduled(clientScheduleId);
|
boolean ok = ((BroadcastModule) mod).cancelScheduled(clientScheduleId);
|
||||||
if (ok) sendHttpResponse(out, "{\"success\":true}", 200);
|
if (ok) sendHttpResponse(out, "{\"success\":true}", 200);
|
||||||
else sendHttpResponse(out, "{\"success\":false,\"error\":\"not_found\"}", 404);
|
else sendHttpResponse(out, "{\"success\":false,\"error\":\"not_found\"}", 404);
|
||||||
return;
|
return;
|
||||||
@@ -259,7 +459,7 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- POST /broadcast oder POST / (Root) für register/send ---
|
// --- POST /broadcast ---
|
||||||
if ("POST".equalsIgnoreCase(method) && ("/broadcast".equalsIgnoreCase(path) || "/".equals(path) || path.isEmpty())) {
|
if ("POST".equalsIgnoreCase(method) && ("/broadcast".equalsIgnoreCase(path) || "/".equals(path) || path.isEmpty())) {
|
||||||
int contentLength = 0;
|
int contentLength = 0;
|
||||||
if (headers.containsKey("content-length")) {
|
if (headers.containsKey("content-length")) {
|
||||||
@@ -277,34 +477,28 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
String body = new String(bodyChars);
|
String body = new String(bodyChars);
|
||||||
|
|
||||||
// Header X-Api-Key (case-insensitive)
|
|
||||||
String apiKeyHeader = headers.getOrDefault("x-api-key", headers.getOrDefault("x-apikey", ""));
|
String apiKeyHeader = headers.getOrDefault("x-api-key", headers.getOrDefault("x-apikey", ""));
|
||||||
|
|
||||||
// parse minimal JSON fields we need
|
|
||||||
String message = extractJsonString(body, "message");
|
String message = extractJsonString(body, "message");
|
||||||
String type = extractJsonString(body, "type");
|
String type = extractJsonString(body, "type");
|
||||||
String prefix = extractJsonString(body, "prefix");
|
String prefix = extractJsonString(body, "prefix");
|
||||||
String prefixColor = extractJsonString(body, "prefixColor");
|
String prefixColor = extractJsonString(body, "prefixColor");
|
||||||
String bracketColor = extractJsonString(body, "bracketColor"); // HINZUGEFÜGT
|
String bracketColor = extractJsonString(body, "bracketColor");
|
||||||
String messageColor = extractJsonString(body, "messageColor");
|
String messageColor = extractJsonString(body, "messageColor");
|
||||||
String sourceName = extractJsonString(body, "source");
|
String sourceName = extractJsonString(body, "source");
|
||||||
String scheduleTimeStr = extractJsonString(body, "scheduleTime"); // expecting millis OR seconds
|
String scheduleTimeStr = extractJsonString(body, "scheduleTime");
|
||||||
String recur = extractJsonString(body, "recur");
|
String recur = extractJsonString(body, "recur");
|
||||||
String clientScheduleId = extractJsonString(body, "clientScheduleId");
|
String clientScheduleId = extractJsonString(body, "clientScheduleId");
|
||||||
|
|
||||||
if (sourceName == null || sourceName.isEmpty()) sourceName = "PulseCast";
|
if (sourceName == null || sourceName.isEmpty()) sourceName = "PulseCast";
|
||||||
if (type == null || type.isEmpty()) type = "global";
|
if (type == null || type.isEmpty()) type = "global";
|
||||||
|
|
||||||
// if scheduleTime present -> register schedule with BroadcastModule
|
|
||||||
if (scheduleTimeStr != null && !scheduleTimeStr.trim().isEmpty()) {
|
if (scheduleTimeStr != null && !scheduleTimeStr.trim().isEmpty()) {
|
||||||
long scheduleMillis = 0L;
|
long scheduleMillis = 0L;
|
||||||
try {
|
try {
|
||||||
// scheduleTime may be numeric string (ms or s)
|
|
||||||
scheduleMillis = Long.parseLong(scheduleTimeStr.trim());
|
scheduleMillis = Long.parseLong(scheduleTimeStr.trim());
|
||||||
// if looks like seconds (less than 1e12), convert to ms
|
|
||||||
if (scheduleMillis < 1_000_000_000_000L) scheduleMillis = scheduleMillis * 1000L;
|
if (scheduleMillis < 1_000_000_000_000L) scheduleMillis = scheduleMillis * 1000L;
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
// try to parse as double then convert
|
|
||||||
try {
|
try {
|
||||||
double d = Double.parseDouble(scheduleTimeStr.trim());
|
double d = Double.parseDouble(scheduleTimeStr.trim());
|
||||||
long v = (long) d;
|
long v = (long) d;
|
||||||
@@ -316,47 +510,12 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastModule: scheduleBroadcast(timestampMillis, sourceName, message, type, apiKeyHeader, prefix, prefixColor, bracketColor, messageColor, recur, clientId)
|
|
||||||
try {
|
try {
|
||||||
Object mod = moduleManager.getModule("BroadcastModule");
|
Object mod = moduleManager.getModule("BroadcastModule");
|
||||||
if (mod instanceof net.viper.status.modules.broadcast.BroadcastModule) {
|
if (mod instanceof BroadcastModule) {
|
||||||
net.viper.status.modules.broadcast.BroadcastModule bm =
|
BroadcastModule bm = (BroadcastModule) mod;
|
||||||
(net.viper.status.modules.broadcast.BroadcastModule) mod;
|
|
||||||
|
|
||||||
boolean ok = bm.scheduleBroadcast(scheduleMillis, sourceName, message, type, apiKeyHeader,
|
boolean ok = bm.scheduleBroadcast(scheduleMillis, sourceName, message, type, apiKeyHeader,
|
||||||
prefix, prefixColor, bracketColor, messageColor, (recur == null ? "none" : recur), (clientScheduleId == null ? null : clientScheduleId)); // bracketColor HINZUGEFÜGT
|
prefix, prefixColor, bracketColor, messageColor, (recur == null ? "none" : recur), (clientScheduleId == null ? null : clientScheduleId));
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
sendHttpResponse(out, "{\"success\":true}", 200);
|
|
||||||
} else {
|
|
||||||
sendHttpResponse(out, "{\"success\":false,\"error\":\"rejected\"}", 403);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
sendHttpResponse(out, "{\"success\":false,\"error\":\"no_broadcast_module\"}", 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
sendHttpResponse(out, "{\"success\":false,\"error\":\"internal\"}", 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no scheduleTime -> immediate broadcast
|
|
||||||
if (message == null || message.isEmpty()) {
|
|
||||||
String resp = "{\"success\":false,\"error\":\"missing_message\"}";
|
|
||||||
sendHttpResponse(out, resp, 400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Object mod = moduleManager.getModule("BroadcastModule");
|
|
||||||
if (mod instanceof net.viper.status.modules.broadcast.BroadcastModule) {
|
|
||||||
net.viper.status.modules.broadcast.BroadcastModule bm =
|
|
||||||
(net.viper.status.modules.broadcast.BroadcastModule) mod;
|
|
||||||
|
|
||||||
boolean ok = bm.handleBroadcast(sourceName, message, type, apiKeyHeader, prefix, prefixColor, bracketColor, messageColor); // bracketColor HINZUGEFÜGT
|
|
||||||
|
|
||||||
if (ok) sendHttpResponse(out, "{\"success\":true}", 200);
|
if (ok) sendHttpResponse(out, "{\"success\":true}", 200);
|
||||||
else sendHttpResponse(out, "{\"success\":false,\"error\":\"rejected\"}", 403);
|
else sendHttpResponse(out, "{\"success\":false,\"error\":\"rejected\"}", 403);
|
||||||
@@ -372,12 +531,37 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- bisheriger GET-Handler (unverändert) ---
|
if (message == null || message.isEmpty()) {
|
||||||
|
String resp = "{\"success\":false,\"error\":\"missing_message\"}";
|
||||||
|
sendHttpResponse(out, resp, 400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object mod = moduleManager.getModule("BroadcastModule");
|
||||||
|
if (mod instanceof BroadcastModule) {
|
||||||
|
BroadcastModule bm = (BroadcastModule) mod;
|
||||||
|
boolean ok = bm.handleBroadcast(sourceName, message, type, apiKeyHeader, prefix, prefixColor, bracketColor, messageColor);
|
||||||
|
|
||||||
|
if (ok) sendHttpResponse(out, "{\"success\":true}", 200);
|
||||||
|
else sendHttpResponse(out, "{\"success\":false,\"error\":\"rejected\"}", 403);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
sendHttpResponse(out, "{\"success\":false,\"error\":\"no_broadcast_module\"}", 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
sendHttpResponse(out, "{\"success\":false,\"error\":\"internal\"}", 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GET Handler ---
|
||||||
if (inputLine != null && inputLine.startsWith("GET")) {
|
if (inputLine != null && inputLine.startsWith("GET")) {
|
||||||
Map<String, Object> data = new LinkedHashMap<>();
|
Map<String, Object> data = new LinkedHashMap<>();
|
||||||
data.put("online", true);
|
data.put("online", true);
|
||||||
|
|
||||||
// Version & Info
|
|
||||||
String versionRaw = ProxyServer.getInstance().getVersion();
|
String versionRaw = ProxyServer.getInstance().getVersion();
|
||||||
String versionClean = (versionRaw != null && versionRaw.contains(":")) ? versionRaw.split(":")[2].trim() : versionRaw;
|
String versionClean = (versionRaw != null && versionRaw.contains(":")) ? versionRaw.split(":")[2].trim() : versionRaw;
|
||||||
data.put("version", versionClean);
|
data.put("version", versionClean);
|
||||||
@@ -390,7 +574,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
data.put("motd", motd);
|
data.put("motd", motd);
|
||||||
|
|
||||||
// StatsModul holen (Service Locator)
|
|
||||||
StatsModule statsModule = (StatsModule) moduleManager.getModule("StatsModule");
|
StatsModule statsModule = (StatsModule) moduleManager.getModule("StatsModule");
|
||||||
|
|
||||||
boolean luckPermsEnabled = ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null;
|
boolean luckPermsEnabled = ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null;
|
||||||
@@ -420,7 +603,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
playerInfo.put("prefix", prefix);
|
playerInfo.put("prefix", prefix);
|
||||||
|
|
||||||
// Stats Integration via Modul
|
|
||||||
if (statsModule != null) {
|
if (statsModule != null) {
|
||||||
PlayerStats ps = statsModule.getManager().getIfPresent(p.getUniqueId());
|
PlayerStats ps = statsModule.getManager().getIfPresent(p.getUniqueId());
|
||||||
if (ps != null) {
|
if (ps != null) {
|
||||||
@@ -451,10 +633,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimaler JSON-String-Extractor (robust genug für einfache Payloads).
|
|
||||||
* Liefert null, wenn Schlüssel nicht gefunden oder kein String-Wert.
|
|
||||||
*/
|
|
||||||
private String extractJsonString(String json, String key) {
|
private String extractJsonString(String json, String key) {
|
||||||
if (json == null || key == null) return null;
|
if (json == null || key == null) return null;
|
||||||
String search = "\"" + key + "\"";
|
String search = "\"" + key + "\"";
|
||||||
@@ -463,12 +641,11 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
int colon = json.indexOf(':', idx + search.length());
|
int colon = json.indexOf(':', idx + search.length());
|
||||||
if (colon < 0) return null;
|
if (colon < 0) return null;
|
||||||
int i = colon + 1;
|
int i = colon + 1;
|
||||||
// skip spaces
|
|
||||||
while (i < json.length() && Character.isWhitespace(json.charAt(i))) i++;
|
while (i < json.length() && Character.isWhitespace(json.charAt(i))) i++;
|
||||||
if (i >= json.length()) return null;
|
if (i >= json.length()) return null;
|
||||||
char c = json.charAt(i);
|
char c = json.charAt(i);
|
||||||
if (c == '"') {
|
if (c == '"') {
|
||||||
i++; // skip opening quote
|
i++;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
boolean escape = false;
|
boolean escape = false;
|
||||||
while (i < json.length()) {
|
while (i < json.length()) {
|
||||||
@@ -484,7 +661,6 @@ public class StatusAPI extends Plugin implements Runnable {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
} else {
|
} else {
|
||||||
// not a quoted string -> read until comma or }
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
while (i < json.length()) {
|
while (i < json.length()) {
|
||||||
char ch = json.charAt(i);
|
char ch = json.charAt(i);
|
||||||
|
|||||||
Reference in New Issue
Block a user