Update from Git Manager GUI

This commit is contained in:
2026-02-23 13:06:59 +01:00
parent c8d4578fa6
commit 02811bafbd
9 changed files with 1050 additions and 174 deletions

View File

@@ -1,6 +1,7 @@
package de.ticketsystem.commands;
import de.ticketsystem.TicketPlugin;
import de.ticketsystem.model.FaqEntry;
import de.ticketsystem.model.Ticket;
import de.ticketsystem.manager.CategoryManager;
@@ -35,27 +36,152 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
if (args.length == 0) { plugin.getTicketManager().sendHelpMessage(player); return true; }
switch (args[0].toLowerCase()) {
case "create" -> handleCreate(player, args);
case "list" -> handleList(player);
case "claim" -> handleClaim(player, args);
case "close" -> handleClose(player, args);
case "forward" -> handleForward(player, args);
case "reload" -> handleReload(player);
case "migrate" -> handleMigrate(player, args);
case "export" -> handleExport(player, args);
case "import" -> handleImport(player, args);
case "stats" -> handleStats(player);
case "top" -> handleTop(player);
case "archive" -> handleArchive(player);
case "comment" -> handleComment(player, args);
case "blacklist" -> handleBlacklist(player, args);
case "rate" -> handleRate(player, args);
case "create" -> handleCreate(player, args);
case "list" -> handleList(player);
case "claim" -> handleClaim(player, args);
case "close" -> handleClose(player, args);
case "forward" -> handleForward(player, args);
case "reload" -> handleReload(player);
case "migrate" -> handleMigrate(player, args);
case "export" -> handleExport(player, args);
case "import" -> handleImport(player, args);
case "stats" -> handleStats(player);
case "top" -> handleTop(player);
case "archive" -> handleArchive(player);
case "comment" -> handleComment(player, args);
case "blacklist" -> handleBlacklist(player, args);
case "rate" -> handleRate(player, args);
case "setpriority" -> handleSetPriority(player, args);
default -> plugin.getTicketManager().sendHelpMessage(player);
case "faq" -> handleFaq(player, args);
default -> plugin.getTicketManager().sendHelpMessage(player);
}
return true;
}
// ─────────────────────────── /ticket faq ───────────────────────────────
/**
* /ticket faq öffnet die FAQ-GUI (alle Spieler)
* /ticket faq add <Frage> | <Antwort> fügt ein FAQ hinzu (ticket.admin)
* /ticket faq edit <ID> <Frage> | <Antwort> bearbeitet ein FAQ (ticket.admin)
* /ticket faq delete <ID> löscht ein FAQ (ticket.admin)
* /ticket faq reload lädt FAQs neu (ticket.admin)
*/
private void handleFaq(Player player, String[] args) {
// Kein Subbefehl → GUI öffnen
if (args.length == 1) {
plugin.getFaqGUI().openFaqGUI(player);
return;
}
switch (args[1].toLowerCase()) {
// ── /ticket faq add <Frage> | <Antwort> ────────────────────────
case "add" -> {
if (!player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
if (args.length < 3) {
player.sendMessage(plugin.color("&cBenutzung: /ticket faq add <Frage> | <Antwort>"));
player.sendMessage(plugin.color("&7Beispiel: &e/ticket faq add Wie erstelle ich ein Ticket? | Nutze /ticket create."));
return;
}
String full = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
String[] parts = full.split("\\s*\\|\\s*", 2);
if (parts.length < 2 || parts[0].isBlank() || parts[1].isBlank()) {
player.sendMessage(plugin.color("&cTrenne Frage und Antwort mit &e|&c, z.B.:"));
player.sendMessage(plugin.color("&e/ticket faq add Wie erstelle ich ein Ticket? | Nutze /ticket create."));
return;
}
FaqEntry created = plugin.getFaqManager().add(parts[0].trim(), parts[1].trim());
player.sendMessage(plugin.color("&aFAQ &e#" + created.getId() + " &awurde erfolgreich erstellt!"));
player.sendMessage(plugin.color("&7Frage: &e" + created.getQuestion()));
player.sendMessage(plugin.color("&7Antwort: &f" + created.getAnswer()));
}
// ── /ticket faq edit <ID> <Frage> | <Antwort> ──────────────────
case "edit" -> {
if (!player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
if (args.length < 4) {
player.sendMessage(plugin.color("&cBenutzung: /ticket faq edit <ID> <Frage> | <Antwort>"));
return;
}
int id;
try { id = Integer.parseInt(args[2]); }
catch (NumberFormatException e) {
player.sendMessage(plugin.color("&cUngültige FAQ-ID: &e" + args[2])); return;
}
String full = String.join(" ", Arrays.copyOfRange(args, 3, args.length));
String[] parts = full.split("\\s*\\|\\s*", 2);
if (parts.length < 2 || parts[0].isBlank() || parts[1].isBlank()) {
player.sendMessage(plugin.color("&cTrenne Frage und Antwort mit &e|&c."));
return;
}
boolean ok = plugin.getFaqManager().edit(id, parts[0].trim(), parts[1].trim());
if (ok) player.sendMessage(plugin.color("&aFAQ &e#" + id + " &awurde erfolgreich aktualisiert!"));
else player.sendMessage(plugin.color("&cFAQ &e#" + id + " &cwurde nicht gefunden."));
}
// ── /ticket faq delete <ID> ─────────────────────────────────────
case "delete", "remove" -> {
if (!player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
if (args.length < 3) {
player.sendMessage(plugin.color("&cBenutzung: /ticket faq delete <ID>")); return;
}
int id;
try { id = Integer.parseInt(args[2]); }
catch (NumberFormatException e) {
player.sendMessage(plugin.color("&cUngültige FAQ-ID: &e" + args[2])); return;
}
boolean ok = plugin.getFaqManager().delete(id);
if (ok) player.sendMessage(plugin.color("&aFAQ &e#" + id + " &awurde gelöscht."));
else player.sendMessage(plugin.color("&cFAQ &e#" + id + " &cwurde nicht gefunden."));
}
// ── /ticket faq reload ──────────────────────────────────────────
case "reload" -> {
if (!player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
plugin.getFaqManager().reload();
player.sendMessage(plugin.color("&aFAQs wurden neu geladen. ("
+ plugin.getFaqManager().getAll().size() + " Einträge)"));
}
// ── /ticket faq list ────────────────────────────────────────────
case "list" -> {
List<FaqEntry> all = plugin.getFaqManager().getAll();
player.sendMessage(plugin.color("&8&m "));
player.sendMessage(plugin.color("&6Häufige Fragen (FAQ) &7— " + all.size() + " Einträge"));
player.sendMessage(plugin.color("&8&m "));
if (all.isEmpty()) {
player.sendMessage(plugin.color("&7Noch keine FAQs vorhanden."));
} else {
for (FaqEntry e : all) {
player.sendMessage(plugin.color("&e#" + e.getId() + " &f" + e.getQuestion()));
player.sendMessage(plugin.color(" &7→ &f" + e.getAnswer()));
}
}
player.sendMessage(plugin.color("&8&m "));
if (player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.color("&7Befehle: &e/ticket faq add &8| &e/ticket faq edit <ID> &8| &e/ticket faq delete <ID>"));
}
}
default -> {
player.sendMessage(plugin.color("&cUnbekannter FAQ-Befehl."));
player.sendMessage(plugin.color("&7Benutze &e/ticket faq &7zum Öffnen der GUI."));
if (player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.color("&7Admin-Befehle: &e/ticket faq add | edit | delete | reload | list"));
}
}
}
}
// ─────────────────────────── /ticket create ────────────────────────────
private void handleCreate(Player player, String[] args) {
@@ -63,7 +189,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
// Blacklist-Check
if (plugin.getDatabaseManager().isBlacklisted(player.getUniqueId())) {
player.sendMessage(plugin.color("&cDu wurdest vom Ticket-System gesperrt und kannst keine Tickets erstellen."));
return;
@@ -91,7 +216,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
return;
}
// Kategorie und Priorität optional parsen
CategoryManager cm = plugin.getCategoryManager();
ConfigCategory category = cm.getDefault();
TicketPriority priority = TicketPriority.NORMAL;
@@ -147,13 +271,14 @@ 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);
if (id == -1) { player.sendMessage(plugin.color("&cFehler beim Erstellen des Tickets!")); return; }
ticket.setId(id);
// Cache befüllen
plugin.getTicketCache().put(ticket);
plugin.getTicketManager().setCooldown(player.getUniqueId());
Bukkit.getScheduler().runTask(plugin, () -> {
String catInfo = plugin.getConfig().getBoolean("categories-enabled", true)
@@ -194,14 +319,14 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
boolean success = plugin.getDatabaseManager().claimTicket(ticketId, player.getUniqueId(), player.getName());
Bukkit.getScheduler().runTask(plugin, () -> {
if (!success) { player.sendMessage(plugin.formatMessage("messages.already-claimed")); return; }
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
Ticket ticket = getCachedOrFetch(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);
// Teleport beim Annehmen entfernt Teleport nur noch über das GUI-Item möglich.
plugin.getTicketCache().invalidate(ticketId); // Stale cache löschen
});
});
}
@@ -224,10 +349,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
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.
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket != null) plugin.getDatabaseManager().recordClosedTicket(ticket, player.getName());
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.formatMessage("messages.ticket-closed").replace("{id}", String.valueOf(ticketId)));
if (ticket != null) {
@@ -252,29 +376,26 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { player.sendMessage(plugin.color("&cUngültige ID!")); 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."));
player.sendMessage(plugin.color("&7[BungeeCord] Spieler &e" + args[2] + " &7ist auf diesem Server nicht online."));
} else {
player.sendMessage(plugin.color("&cSpieler nicht gefunden!"));
}
return;
}
final int ticketId = id;
final String fromName = player.getName();
final Player t = localTarget;
final int ticketId = id;
final String fromName = player.getName();
final Player t = localTarget;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean success = plugin.getDatabaseManager().forwardTicket(ticketId, t.getUniqueId(), t.getName());
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (!success) { player.sendMessage(plugin.formatMessage("messages.ticket-not-found")); return; }
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) return;
player.sendMessage(plugin.color("&aTicket &e#" + ticketId + " &awurde an &e" + t.getName() + " &aweitergeleitet."));
plugin.getTicketManager().notifyForwardedTo(ticket, fromName);
@@ -297,17 +418,17 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
String msg = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
if (msg.length() > 500) { player.sendMessage(plugin.color("&cNachricht zu lang! Maximal 500 Zeichen.")); return; }
final int ticketId = id;
final String message = msg;
final int ticketId = id;
final String message = msg;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) {
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.formatMessage("messages.ticket-not-found")));
return;
}
boolean isOwner = ticket.getCreatorUUID().equals(player.getUniqueId());
boolean isStaff = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
boolean isOwner = ticket.getCreatorUUID().equals(player.getUniqueId());
boolean isStaff = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
if (!isOwner && !isStaff) {
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.color("&cDu kannst nur deine eigenen Tickets kommentieren.")));
return;
@@ -327,59 +448,27 @@ 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;
// ── 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);
plugin.getBungeeMessenger().sendMessageToPlayer(ticket.getCreatorUUID(), ticket.getCreatorName(), onlineMsg);
} else {
// Standalone: Offline → für nächsten Login speichern
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
plugin.getDatabaseManager().addPendingNotification(ticket.getCreatorUUID(), offlineMsg));
}
}
// ── 2. Supporter/Admin benachrichtigen (wenn Kommentar vom Spieler kommt) ──
if (!author.hasPermission("ticket.support") && !author.hasPermission("ticket.admin")) {
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();
var claimerUUID = ticket.getClaimerUUID();
if (claimerUUID != null && !claimerUUID.equals(author.getUniqueId())) {
Player claimer = Bukkit.getPlayer(claimerUUID);
if (claimer != null && claimer.isOnline()) {
@@ -391,8 +480,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
plugin.getDatabaseManager().addPendingNotification(claimerUUID, claimerOffline));
}
}
// 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;
@@ -426,11 +513,11 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
return;
}
final int ticketId = id;
final int ticketId = id;
final String finalRating = rating;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) {
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.formatMessage("messages.ticket-not-found")));
return;
@@ -445,6 +532,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
}
boolean success = plugin.getDatabaseManager().rateTicket(ticketId, finalRating);
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (success) {
String emoji = "THUMBS_UP".equals(finalRating) ? "§a👍 Positiv" : "§c👎 Negativ";
@@ -478,27 +566,22 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
if (target.getUniqueId() == null) { player.sendMessage(plugin.color("&cSpieler nicht gefunden.")); return; }
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean success = plugin.getDatabaseManager().addBlacklist(
target.getUniqueId(), targetName, reason, player.getName());
boolean success = plugin.getDatabaseManager().addBlacklist(target.getUniqueId(), targetName, reason, player.getName());
Bukkit.getScheduler().runTask(plugin, () -> {
if (success)
player.sendMessage(plugin.color("&a" + targetName + " &awurde zur Ticket-Blacklist hinzugefügt. &7Grund: &e" + reason));
else
player.sendMessage(plugin.color("&cSpieler ist bereits auf der Blacklist."));
if (success) player.sendMessage(plugin.color("&a" + targetName + " &awurde zur Ticket-Blacklist hinzugefügt. &7Grund: &e" + reason));
else player.sendMessage(plugin.color("&cSpieler ist bereits auf der Blacklist."));
});
});
}
case "remove" -> {
if (args.length < 3) { player.sendMessage(plugin.color("&cBenutzung: /ticket blacklist remove <Spieler>")); return; }
String targetName = args[2];
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
OfflinePlayer target = Bukkit.getOfflinePlayer(args[2]);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean success = plugin.getDatabaseManager().removeBlacklist(target.getUniqueId());
Bukkit.getScheduler().runTask(plugin, () -> {
if (success) player.sendMessage(plugin.color("&a" + targetName + " &awurde von der Blacklist entfernt."));
else player.sendMessage(plugin.color("&cSpieler war nicht auf der Blacklist."));
if (success) player.sendMessage(plugin.color("&a" + args[2] + " &awurde von der Blacklist entfernt."));
else player.sendMessage(plugin.color("&cSpieler war nicht auf der Blacklist."));
});
});
}
@@ -527,16 +610,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
// ─────────────────────────── /ticket top ──────────────────────────────
/**
* Zeigt das Leaderboard der Top-5 Ticket-Ersteller.
* Basiert auf der ticket_creator_stats-Tabelle, die Werte auch nach
* dem Löschen oder Archivieren von Tickets beibehält.
* Berechtigung: ticket.create (alle Spieler)
*/
private void handleTop(Player player) {
if (!player.hasPermission("ticket.create") && !player.hasPermission("ticket.admin")) {
player.sendMessage(plugin.formatMessage("messages.no-permission"));
return;
player.sendMessage(plugin.formatMessage("messages.no-permission")); return;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
List<String[]> top = plugin.getDatabaseManager().getTopCreators(5);
@@ -553,8 +629,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
String medal = rankIdx < medals.length ? medals[rankIdx] : "&7#" + entry[0];
String name = entry[1];
String count = entry[2];
player.sendMessage(plugin.color(
medal + " &f" + String.format("%-16s", name)
player.sendMessage(plugin.color(medal + " &f" + String.format("%-16s", name)
+ " &e" + count + " &7Ticket" + (Integer.parseInt(count) == 1 ? "" : "s")));
}
}
@@ -570,7 +645,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
if (!player.hasPermission("ticket.admin")) { player.sendMessage(plugin.formatMessage("messages.no-permission")); return; }
plugin.reloadConfig();
plugin.getCategoryManager().reload();
player.sendMessage(plugin.color("&aKonfiguration wurde neu geladen. &7(inkl. Kategorien)"));
plugin.getFaqManager().reload();
plugin.getTicketCache().clear();
player.sendMessage(plugin.color("&aKonfiguration wurde neu geladen. &7(Kategorien, FAQs, Cache geleert)"));
if (plugin.isBungeeCordEnabled()) {
player.sendMessage(plugin.color("&8[BungeeCord] &7Server: &b" + plugin.getServerName()));
}
@@ -584,7 +661,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
int count = plugin.getDatabaseManager().archiveClosedTickets();
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.formatMessage("messages.archive-success").replace("{count}", String.valueOf(count)));
else player.sendMessage(plugin.formatMessage("messages.archive-fail"));
else player.sendMessage(plugin.formatMessage("messages.archive-fail"));
});
});
}
@@ -596,7 +673,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
var stats = plugin.getDatabaseManager().getTicketStats();
var staffRatings = plugin.getDatabaseManager().getStaffRatings();
// Persistente Ersteller-Statistik überlebt Löschen/Archivieren
var topCreators = plugin.getDatabaseManager().getTopCreators(5);
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.color("&8&m "));
@@ -617,26 +693,21 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
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));
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:"));
@@ -662,6 +733,10 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
}
}
player.sendMessage(plugin.color("&8&m "));
// Cache-Status anzeigen
player.sendMessage(plugin.color("&8&m "));
player.sendMessage(plugin.color("&7Cache: &e" + plugin.getTicketCache().size() + " &7gecachte Ticket(s)"));
});
});
}
@@ -680,7 +755,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
int f = migrated;
Bukkit.getScheduler().runTask(plugin, () -> {
if (f > 0) player.sendMessage(plugin.formatMessage("messages.migration-success").replace("{count}", String.valueOf(f)));
else player.sendMessage(plugin.formatMessage("messages.migration-fail"));
else player.sendMessage(plugin.formatMessage("messages.migration-fail"));
});
});
}
@@ -696,7 +771,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
int count = plugin.getDatabaseManager().exportTickets(exportFile);
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.formatMessage("messages.export-success").replace("{count}", String.valueOf(count)).replace("{file}", filename));
else player.sendMessage(plugin.formatMessage("messages.export-fail"));
else player.sendMessage(plugin.formatMessage("messages.export-fail"));
});
});
}
@@ -713,7 +788,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
int count = plugin.getDatabaseManager().importTickets(importFile);
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.formatMessage("messages.import-success").replace("{count}", String.valueOf(count)));
else player.sendMessage(plugin.formatMessage("messages.import-fail"));
else player.sendMessage(plugin.formatMessage("messages.import-fail"));
});
});
}
@@ -739,20 +814,31 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
if (priority == null) {
player.sendMessage(plugin.color("&cUngültige Priorität! Gültig: &alow&7, &enormal&7, &6high&7, &curgent")); return;
}
final int finalId = ticketId;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean success = plugin.getDatabaseManager().setTicketPriority(ticketId, priority);
boolean success = plugin.getDatabaseManager().setTicketPriority(finalId, priority);
plugin.getTicketCache().invalidate(finalId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (success) {
player.sendMessage(plugin.color("&aPriorität von Ticket &e#" + ticketId
+ " &awurde auf " + priority.getColored() + " &agesetzt."));
} else {
player.sendMessage(plugin.color("&cTicket &e#" + ticketId + " &cwurde nicht gefunden."));
}
if (success) player.sendMessage(plugin.color("&aPriorität von Ticket &e#" + finalId + " &awurde auf " + priority.getColored() + " &agesetzt."));
else player.sendMessage(plugin.color("&cTicket &e#" + finalId + " &cwurde nicht gefunden."));
});
});
}
/** Parst Benutzer-Eingaben zu TicketPriority. Gibt null zurück wenn keine Übereinstimmung. */
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
/**
* Gibt ein Ticket aus dem Cache zurück, oder lädt es aus der Datenbank
* und legt es anschließend in den Cache. Gibt null zurück wenn nicht gefunden.
*/
private Ticket getCachedOrFetch(int ticketId) {
Ticket cached = plugin.getTicketCache().get(ticketId);
if (cached != null) return cached;
Ticket fresh = plugin.getDatabaseManager().getTicketById(ticketId);
if (fresh != null) plugin.getTicketCache().put(fresh);
return fresh;
}
private TicketPriority parsePriority(String input) {
if (input == null) return null;
return switch (input.toLowerCase()) {
@@ -772,7 +858,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
if (!(sender instanceof Player player)) return completions;
if (args.length == 1) {
List<String> subs = new ArrayList<>(List.of("create", "list", "comment", "top"));
List<String> subs = new ArrayList<>(List.of("create", "list", "comment", "top", "faq"));
if (player.hasPermission("ticket.support") || player.hasPermission("ticket.admin"))
subs.addAll(List.of("claim", "close"));
if (plugin.getConfig().getBoolean("rating-enabled", true)) subs.add("rate");
@@ -783,6 +869,17 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
subs.add("setpriority");
for (String s : subs) if (s.startsWith(args[0].toLowerCase())) completions.add(s);
} else if (args.length == 2 && args[0].equalsIgnoreCase("faq")) {
List<String> faqSubs = new ArrayList<>(List.of("list"));
if (player.hasPermission("ticket.admin")) faqSubs.addAll(List.of("add", "edit", "delete", "reload"));
for (String s : faqSubs) if (s.startsWith(args[1].toLowerCase())) completions.add(s);
} else if (args.length == 3 && args[0].equalsIgnoreCase("faq")
&& (args[1].equalsIgnoreCase("edit") || args[1].equalsIgnoreCase("delete"))
&& player.hasPermission("ticket.admin")) {
for (FaqEntry e : plugin.getFaqManager().getAll())
completions.add(String.valueOf(e.getId()));
} else if (args.length == 2 && args[0].equalsIgnoreCase("create")
&& plugin.getConfig().getBoolean("categories-enabled", true)) {
for (ConfigCategory c : plugin.getCategoryManager().getAll())
@@ -801,7 +898,6 @@ 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());