Update from Git Manager GUI
This commit is contained in:
@@ -100,13 +100,11 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
|
||||
if (args.length >= 3) {
|
||||
// args[1]: erst als Kategorie prüfen, dann als Priorität
|
||||
if (categoriesOn) {
|
||||
ConfigCategory parsedCat = cm.resolve(args[1]);
|
||||
if (parsedCat != null) {
|
||||
category = parsedCat;
|
||||
messageStartIndex = 2;
|
||||
// args[2]: Priorität prüfen (nur wenn danach noch Text kommt)
|
||||
if (prioritiesOn && args.length >= 4) {
|
||||
TicketPriority parsedPrio = parsePriority(args[2]);
|
||||
if (parsedPrio != null) {
|
||||
@@ -115,7 +113,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Keine Kategorie erkannt → args[1] als Priorität prüfen
|
||||
if (prioritiesOn) {
|
||||
TicketPriority parsedPrio = parsePriority(args[1]);
|
||||
if (parsedPrio != null) {
|
||||
@@ -125,16 +122,12 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
} else if (prioritiesOn) {
|
||||
// Kategorien aus → args[1] direkt als Priorität prüfen
|
||||
TicketPriority parsedPrio = parsePriority(args[1]);
|
||||
if (parsedPrio != null) {
|
||||
priority = parsedPrio;
|
||||
messageStartIndex = 2;
|
||||
}
|
||||
}
|
||||
} else if (args.length == 2) {
|
||||
// Nur ein Argument: könnte Kategorie oder Priorität sein, aber kein Text danach
|
||||
// → einfach als Beschreibung behandeln, nichts parsen
|
||||
}
|
||||
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, messageStartIndex, args.length));
|
||||
@@ -153,6 +146,8 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
Ticket ticket = new Ticket(player.getUniqueId(), player.getName(), message, player.getLocation());
|
||||
ticket.setCategoryKey(finalCategory.getKey());
|
||||
ticket.setPriority(finalPriority);
|
||||
// BungeeCord: Server-Name des erstellenden Servers speichern
|
||||
ticket.setServerName(plugin.getServerName());
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int id = plugin.getDatabaseManager().createTicket(ticket);
|
||||
@@ -200,11 +195,31 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
if (!success) { player.sendMessage(plugin.formatMessage("messages.already-claimed")); return; }
|
||||
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
||||
if (ticket == null) return;
|
||||
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-claimed")
|
||||
.replace("{id}", String.valueOf(ticketId))
|
||||
.replace("{player}", ticket.getCreatorName()));
|
||||
plugin.getTicketManager().notifyCreatorClaimed(ticket);
|
||||
if (ticket.getLocation() != null) player.teleport(ticket.getLocation());
|
||||
|
||||
// ── BUG FIX #1: Teleportation bei aktivem BungeeCord komplett sperren ──
|
||||
// Wenn BungeeCord aktiv ist, kann das Ticket von einem anderen Server stammen.
|
||||
// getLocation() würde null liefern (World existiert lokal nicht) oder den
|
||||
// Supporter auf dem falschen Server teleportieren.
|
||||
// Lösung: Bei aktivem BungeeCord generell keinen Teleport durchführen.
|
||||
if (plugin.isBungeeCordEnabled()) {
|
||||
// Hinweis: Server anzeigen wenn bekannt, damit Supporter weiß wo das Ticket ist
|
||||
String serverHint = !"unknown".equals(ticket.getServerName())
|
||||
? " &7(Server: &b" + ticket.getServerName() + "&7)"
|
||||
: "";
|
||||
player.sendMessage(plugin.color("&7Teleportation deaktiviert – BungeeCord-Netzwerk aktiv." + serverHint));
|
||||
} else {
|
||||
// Standalone-Modus: Normal teleportieren
|
||||
if (ticket.getLocation() != null) {
|
||||
player.teleport(ticket.getLocation());
|
||||
} else {
|
||||
player.sendMessage(plugin.color("&7Teleportation nicht möglich – World nicht gefunden."));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -228,6 +243,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
boolean success = plugin.getDatabaseManager().closeTicket(ticketId, comment);
|
||||
if (success) {
|
||||
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
||||
// Ticket in persistente Stats-Tabelle eintragen (bleibt auch nach Löschung erhalten).
|
||||
// player.getName() = der Admin der /ticket close ausgeführt hat – nicht zwingend der Claimer.
|
||||
if (ticket != null) plugin.getDatabaseManager().recordClosedTicket(ticket, player.getName());
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage(plugin.formatMessage("messages.ticket-closed").replace("{id}", String.valueOf(ticketId)));
|
||||
if (ticket != null) {
|
||||
@@ -252,12 +270,23 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
try { id = Integer.parseInt(args[1]); }
|
||||
catch (NumberFormatException e) { player.sendMessage(plugin.color("&cUngültige ID!")); return; }
|
||||
|
||||
Player target = Bukkit.getPlayer(args[2]);
|
||||
if (target == null) { player.sendMessage(plugin.color("&cSpieler nicht gefunden!")); return; }
|
||||
// BungeeCord: Ziel-Spieler lokal suchen
|
||||
Player localTarget = Bukkit.getPlayer(args[2]);
|
||||
|
||||
if (localTarget == null) {
|
||||
if (plugin.isBungeeCordEnabled()) {
|
||||
player.sendMessage(plugin.color("&7[BungeeCord] Spieler &e" + args[2]
|
||||
+ " &7ist auf diesem Server nicht online."));
|
||||
player.sendMessage(plugin.color("&7Tipp: Forwarden geht nur zu Spielern auf &bdemselben Server&7."));
|
||||
} else {
|
||||
player.sendMessage(plugin.color("&cSpieler nicht gefunden!"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int ticketId = id;
|
||||
final String fromName = player.getName();
|
||||
final Player t = target;
|
||||
final Player t = localTarget;
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
boolean success = plugin.getDatabaseManager().forwardTicket(ticketId, t.getUniqueId(), t.getName());
|
||||
@@ -295,7 +324,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.formatMessage("messages.ticket-not-found")));
|
||||
return;
|
||||
}
|
||||
// Spieler darf nur auf eigene Tickets kommentieren (Supporter/Admin: alle)
|
||||
boolean isOwner = ticket.getCreatorUUID().equals(player.getUniqueId());
|
||||
boolean isStaff = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
|
||||
if (!isOwner && !isStaff) {
|
||||
@@ -309,7 +337,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (success) {
|
||||
player.sendMessage(plugin.color("&aDein Kommentar zu Ticket &e#" + ticketId + " &awurde gespeichert."));
|
||||
// Supporter/Admin und Ticket-Ersteller benachrichtigen
|
||||
notifyCommentReceivers(player, ticket, message);
|
||||
} else {
|
||||
player.sendMessage(plugin.color("&cFehler beim Speichern des Kommentars."));
|
||||
@@ -318,42 +345,78 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Benachrichtigt alle relevanten Empfänger über einen neuen Kommentar.
|
||||
*
|
||||
* ── BUG FIX #2 ──────────────────────────────────────────────────────────
|
||||
* Vorher: broadcastTeamNotification() wurde am Ende ZUSÄTZLICH aufgerufen –
|
||||
* obwohl alle lokalen Supporter bereits einzeln per Schleife
|
||||
* benachrichtigt wurden. Das führte zu:
|
||||
* a) Doppelter Nachricht für lokale Supporter
|
||||
* b) broadcastTeamNotification() sendet intern ebenfalls lokal →
|
||||
* lokale Supporter sahen die Nachricht dreifach
|
||||
* c) Das Forward-Paket an andere Server war korrekt, aber die
|
||||
* Empfänger auf anderen Servern sahen auch Duplikate da
|
||||
* broadcastTeamNotification() wiederum lokal sendet
|
||||
*
|
||||
* Fix: broadcastTeamNotification() ERSETZT die lokale Supporter-Schleife
|
||||
* komplett. Die Methode sendet bereits lokal direkt und forwardet
|
||||
* gleichzeitig an alle anderen BungeeCord-Server.
|
||||
* Im Standalone-Modus bleibt die lokale Schleife erhalten.
|
||||
* ────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
private void notifyCommentReceivers(Player author, Ticket ticket, String message) {
|
||||
String onlineMsg = plugin.color("&e[Ticket #" + ticket.getId() + "] &f" + author.getName() + " &7hat kommentiert: &f" + message);
|
||||
String offlineMsg = "&e[Ticket #" + ticket.getId() + "] &f" + author.getName() + " &7hat kommentiert (während du offline warst): &f" + message;
|
||||
|
||||
// Ticket-Ersteller benachrichtigen (wenn nicht der Autor selbst)
|
||||
// ── 1. Ticket-Ersteller benachrichtigen (wenn nicht der Autor selbst) ──
|
||||
if (!ticket.getCreatorUUID().equals(author.getUniqueId())) {
|
||||
Player creator = Bukkit.getPlayer(ticket.getCreatorUUID());
|
||||
if (creator != null && creator.isOnline()) {
|
||||
creator.sendMessage(onlineMsg);
|
||||
} else if (plugin.isBungeeCordEnabled()) {
|
||||
// BungeeCord: Zustellung via Plugin-Messaging, kein Pending-Eintrag
|
||||
// (PlayerJoinListener übernimmt Offline-Fallback via close_notified-Logik)
|
||||
plugin.getBungeeMessenger().sendMessageToPlayer(
|
||||
ticket.getCreatorUUID(), ticket.getCreatorName(), onlineMsg);
|
||||
} else {
|
||||
// Offline → für nächsten Login speichern
|
||||
// Standalone: Offline → für nächsten Login speichern
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
|
||||
plugin.getDatabaseManager().addPendingNotification(ticket.getCreatorUUID(), offlineMsg));
|
||||
}
|
||||
}
|
||||
|
||||
// Supporter/Admin benachrichtigen (wenn Autor kein Supporter ist)
|
||||
// ── 2. Supporter/Admin benachrichtigen (wenn Kommentar vom Spieler kommt) ──
|
||||
if (!author.hasPermission("ticket.support") && !author.hasPermission("ticket.admin")) {
|
||||
// Claimer des Tickets bevorzugt benachrichtigen
|
||||
UUID claimerUUID = ticket.getClaimerUUID();
|
||||
if (claimerUUID != null && !claimerUUID.equals(author.getUniqueId())) {
|
||||
Player claimer = Bukkit.getPlayer(claimerUUID);
|
||||
if (claimer != null && claimer.isOnline()) {
|
||||
claimer.sendMessage(onlineMsg);
|
||||
} else {
|
||||
String claimerOffline = "&e[Ticket #" + ticket.getId() + "] &f" + author.getName() + " &7hat auf dein bearbeitetes Ticket kommentiert (offline): &f" + message;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
|
||||
plugin.getDatabaseManager().addPendingNotification(claimerUUID, claimerOffline));
|
||||
|
||||
if (plugin.isBungeeCordEnabled()) {
|
||||
// BungeeCord-Modus: broadcastTeamNotification() übernimmt ALLES –
|
||||
// lokal direkt + Forward an alle anderen Server in einem Paket.
|
||||
// KEINE zusätzliche lokale Schleife, da das zu Duplikaten führt.
|
||||
plugin.getBungeeMessenger().broadcastTeamNotification(onlineMsg);
|
||||
|
||||
} else {
|
||||
// Standalone-Modus: Claimer gezielt benachrichtigen
|
||||
UUID claimerUUID = ticket.getClaimerUUID();
|
||||
if (claimerUUID != null && !claimerUUID.equals(author.getUniqueId())) {
|
||||
Player claimer = Bukkit.getPlayer(claimerUUID);
|
||||
if (claimer != null && claimer.isOnline()) {
|
||||
claimer.sendMessage(onlineMsg);
|
||||
} else {
|
||||
String claimerOffline = "&e[Ticket #" + ticket.getId() + "] &f" + author.getName()
|
||||
+ " &7hat auf dein bearbeitetes Ticket kommentiert (offline): &f" + message;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
|
||||
plugin.getDatabaseManager().addPendingNotification(claimerUUID, claimerOffline));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Alle anderen Online-Supporter zusätzlich informieren
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.getUniqueId().equals(author.getUniqueId())) continue;
|
||||
if (claimerUUID != null && p.getUniqueId().equals(claimerUUID)) continue; // schon oben
|
||||
if (p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")) {
|
||||
p.sendMessage(onlineMsg);
|
||||
|
||||
// Alle anderen Online-Supporter auf diesem Server informieren
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.getUniqueId().equals(author.getUniqueId())) continue;
|
||||
if (claimerUUID != null && p.getUniqueId().equals(claimerUUID)) continue;
|
||||
if (p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")) {
|
||||
p.sendMessage(onlineMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,7 +531,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
player.sendMessage(plugin.color("&7Keine gesperrten Spieler."));
|
||||
} else {
|
||||
for (String[] entry : list) {
|
||||
// {uuid, name, reason, bannedBy, bannedAt}
|
||||
player.sendMessage(plugin.color("&e" + entry[1] + " &7– &f" + entry[2]
|
||||
+ " &7(gesperrt von &e" + entry[3] + "&7)"));
|
||||
}
|
||||
@@ -488,6 +550,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
plugin.reloadConfig();
|
||||
plugin.getCategoryManager().reload();
|
||||
player.sendMessage(plugin.color("&aKonfiguration wurde neu geladen. &7(inkl. Kategorien)"));
|
||||
if (plugin.isBungeeCordEnabled()) {
|
||||
player.sendMessage(plugin.color("&8[BungeeCord] &7Server: &b" + plugin.getServerName()));
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── /ticket archive ───────────────────────────
|
||||
@@ -508,26 +573,55 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
private void handleStats(Player player) {
|
||||
if (!player.hasPermission("ticket.admin")) { player.sendMessage(plugin.formatMessage("messages.no-permission")); return; }
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
var stats = plugin.getDatabaseManager().getTicketStats();
|
||||
var stats = plugin.getDatabaseManager().getTicketStats();
|
||||
var staffRatings = plugin.getDatabaseManager().getStaffRatings();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6Ticket Statistik"));
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&eGesamt: &a" + stats.total));
|
||||
player.sendMessage(plugin.color("&eOffen: &a" + stats.open));
|
||||
player.sendMessage(plugin.color("&eGeschlossen: &a" + stats.closed));
|
||||
player.sendMessage(plugin.color("&eGesamt: &a" + stats.total));
|
||||
player.sendMessage(plugin.color("&eOffen: &a" + stats.open));
|
||||
player.sendMessage(plugin.color("&eGeschlossen: &a" + stats.closed + " &7(historisch)"));
|
||||
player.sendMessage(plugin.color("&eWeitergeleitet: &a" + stats.forwarded));
|
||||
|
||||
if (plugin.getConfig().getBoolean("rating-enabled", true)) {
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6Support-Bewertungen"));
|
||||
player.sendMessage(plugin.color("&6Support-Bewertungen &7(gesamt, historisch)"));
|
||||
player.sendMessage(plugin.color("&a👍 Positiv: &f" + stats.thumbsUp
|
||||
+ " &c👎 Negativ: &f" + stats.thumbsDown));
|
||||
int total = stats.thumbsUp + stats.thumbsDown;
|
||||
if (total > 0) {
|
||||
int percent = (int) Math.round(stats.thumbsUp * 100.0 / total);
|
||||
int totalRated = stats.thumbsUp + stats.thumbsDown;
|
||||
if (totalRated > 0) {
|
||||
int percent = (int) Math.round(stats.thumbsUp * 100.0 / totalRated);
|
||||
player.sendMessage(plugin.color("&7Zufriedenheit: &e" + percent + "%"));
|
||||
}
|
||||
|
||||
// Bewertungen pro Support-Mitarbeiter
|
||||
if (!staffRatings.isEmpty()) {
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6Bewertungen nach Support-Mitarbeiter:"));
|
||||
player.sendMessage(plugin.color("&7 Name 👍 👎 Tickets Zufrieden"));
|
||||
for (String[] row : staffRatings) {
|
||||
// row: [name, up, down, totalClosed, percent]
|
||||
String name = String.format("%-16s", row[0]);
|
||||
String up = String.format("%-5s", row[1]);
|
||||
String down = String.format("%-5s", row[2]);
|
||||
String total = String.format("%-8s", row[3]);
|
||||
String percent = row[4];
|
||||
player.sendMessage(plugin.color(
|
||||
"&e " + name + " &a" + up + " &c" + down + " &7" + total + " &e" + percent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BungeeCord: Tickets pro Server anzeigen
|
||||
if (plugin.isBungeeCordEnabled() && !stats.byServer.isEmpty()) {
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6Tickets nach Server:"));
|
||||
stats.byServer.entrySet().stream()
|
||||
.sorted((a, b) -> b.getValue() - a.getValue())
|
||||
.forEach(e -> player.sendMessage(plugin.color("&b " + e.getKey() + ": &a" + e.getValue())));
|
||||
}
|
||||
|
||||
player.sendMessage(plugin.color("&8&m "));
|
||||
player.sendMessage(plugin.color("&6Top Ersteller:"));
|
||||
stats.byPlayer.entrySet().stream()
|
||||
@@ -625,8 +719,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
});
|
||||
}
|
||||
|
||||
/** Parst Benutzer-Eingaben wie "high", "hoch", "urgent", "dringend" etc. zu TicketPriority.
|
||||
* Gibt null zurück wenn keine Übereinstimmung. */
|
||||
/** Parst Benutzer-Eingaben zu TicketPriority. Gibt null zurück wenn keine Übereinstimmung. */
|
||||
private TicketPriority parsePriority(String input) {
|
||||
if (input == null) return null;
|
||||
return switch (input.toLowerCase()) {
|
||||
@@ -661,14 +754,12 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
&& plugin.getConfig().getBoolean("categories-enabled", true)) {
|
||||
for (ConfigCategory c : plugin.getCategoryManager().getAll())
|
||||
if (c.getKey().startsWith(args[1].toLowerCase())) completions.add(c.getKey());
|
||||
// auch Priorität direkt ohne Kategorie anbieten
|
||||
if (plugin.getConfig().getBoolean("priorities-enabled", true))
|
||||
for (String p : List.of("low", "normal", "high", "urgent"))
|
||||
if (p.startsWith(args[1].toLowerCase())) completions.add(p);
|
||||
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("create")
|
||||
&& plugin.getConfig().getBoolean("priorities-enabled", true)) {
|
||||
// Priorität nach Kategorie
|
||||
for (String p : List.of("low", "normal", "high", "urgent"))
|
||||
if (p.startsWith(args[2].toLowerCase())) completions.add(p);
|
||||
|
||||
@@ -677,6 +768,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
if (p.startsWith(args[2].toLowerCase())) completions.add(p);
|
||||
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("forward")) {
|
||||
// BungeeCord: Nur lokal online Spieler als Tab-Completion
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
if (p.getName().toLowerCase().startsWith(args[2].toLowerCase())) completions.add(p.getName());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user