Files
TicketSystem/src/main/java/de/ticketsystem/commands/TicketCommand.java
2026-02-25 19:01:41 +01:00

953 lines
52 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package de.ticketsystem.commands;
import de.ticketsystem.TicketPlugin;
import de.ticketsystem.model.ConfigCategory;
import de.ticketsystem.model.FaqEntry;
import de.ticketsystem.model.Ticket;
import de.ticketsystem.model.TicketComment;
import de.ticketsystem.model.TicketPriority;
import de.ticketsystem.manager.CategoryManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TicketCommand implements CommandExecutor, TabCompleter {
private final TicketPlugin plugin;
public TicketCommand(TicketPlugin plugin) {
this.plugin = plugin;
}
// ── Subkommando-Auflösung ─────────────────────────────────────────────
/**
* Normalisiert ein Subkommando auf den internen englischen Schlüssel.
* Deutsche UND englische Varianten werden immer akzeptiert unabhängig
* von der language-Einstellung. So kann ein Admin auf einem EN-Server
* trotzdem den deutschen Begriff tippen ohne einen Fehler zu bekommen.
*/
private String normalize(String input) {
return switch (input.toLowerCase()) {
// Deutsch → intern
case "erstellen" -> "create";
case "liste" -> "list";
case "übernehmen", "uebernehmen",
"beanspruchen" -> "claim";
case "schließen", "schliessen" -> "close";
case "weiterleiten" -> "forward";
case "neuladen" -> "reload";
case "migrieren" -> "migrate";
case "exportieren" -> "export";
case "importieren" -> "import";
case "statistik" -> "stats";
case "archivieren" -> "archive";
case "kommentar" -> "comment";
case "sperrliste" -> "blacklist";
case "bewerten" -> "rate";
case "priorität", "prioritaet" -> "setpriority";
case "hilfe" -> "help";
// Englisch + alles andere → unverändert
default -> input.toLowerCase();
};
}
// ── onCommand ─────────────────────────────────────────────────────────
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(plugin.lang().get("general.console-only"));
return true;
}
if (args.length == 0) {
plugin.getTicketManager().sendHelpMessage(player);
return true;
}
switch (normalize(args[0])) {
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);
case "faq" -> handleFaq(player, args);
default -> plugin.getTicketManager().sendHelpMessage(player);
}
return true;
}
// ── /ticket create ────────────────────────────────────────────────────
private void handleCreate(Player player, String[] args) {
if (!player.hasPermission("ticket.create")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (plugin.getDatabaseManager().isBlacklisted(player.getUniqueId())) {
plugin.lang().send(player, "create.blacklist-blocked"); return;
}
if (args.length < 2) {
player.sendMessage(plugin.lang().get("create.usage"));
if (plugin.getConfig().getBoolean("categories-enabled", true))
player.sendMessage(plugin.lang().get("create.categories-hint"));
if (plugin.getConfig().getBoolean("priorities-enabled", true))
player.sendMessage(plugin.lang().get("create.priorities-hint"));
return;
}
if (plugin.getTicketManager().hasCooldown(player.getUniqueId())) {
long rem = plugin.getTicketManager().getRemainingCooldown(player.getUniqueId());
plugin.lang().send(player, "general.cooldown", "{seconds}", String.valueOf(rem));
return;
}
if (plugin.getTicketManager().hasReachedTicketLimit(player.getUniqueId())) {
int max = plugin.getConfig().getInt("max-open-tickets-per-player", 2);
plugin.lang().send(player, "create.max-tickets", "{max}", String.valueOf(max));
return;
}
CategoryManager cm = plugin.getCategoryManager();
ConfigCategory category = cm.getDefault();
TicketPriority priority = TicketPriority.NORMAL;
int msgStart = 1;
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
if (args.length >= 3) {
if (categoriesOn) {
ConfigCategory parsedCat = cm.resolve(args[1]);
if (parsedCat != null) {
category = parsedCat;
msgStart = 2;
if (prioritiesOn && args.length >= 4) {
TicketPriority parsedPrio = parsePriority(args[2]);
if (parsedPrio != null) { priority = parsedPrio; msgStart = 3; }
}
} else if (prioritiesOn) {
TicketPriority parsedPrio = parsePriority(args[1]);
if (parsedPrio != null) { priority = parsedPrio; msgStart = 2; }
}
} else if (prioritiesOn) {
TicketPriority parsedPrio = parsePriority(args[1]);
if (parsedPrio != null) { priority = parsedPrio; msgStart = 2; }
}
}
String message = String.join(" ", Arrays.copyOfRange(args, msgStart, args.length));
int maxLen = plugin.getConfig().getInt("max-description-length", 100);
if (message.isEmpty()) { plugin.lang().send(player, "create.no-description"); return; }
if (message.length() > maxLen) { plugin.lang().send(player, "create.too-long", "{max}", String.valueOf(maxLen)); return; }
final ConfigCategory fCat = category;
final TicketPriority fPrio = priority;
Ticket ticket = new Ticket(player.getUniqueId(), player.getName(), message, player.getLocation());
ticket.setCategoryKey(fCat.getKey());
ticket.setPriority(fPrio);
ticket.setServerName(plugin.getServerName());
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
int id = plugin.getDatabaseManager().createTicket(ticket);
if (id == -1) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "system.db-create-error"));
return;
}
ticket.setId(id);
plugin.getTicketCache().put(ticket);
plugin.getTicketManager().setCooldown(player.getUniqueId());
Bukkit.getScheduler().runTask(plugin, () -> {
String catInfo = categoriesOn ? " §7[" + fCat.getColored() + "§7]" : "";
String prioInfo = prioritiesOn ? " §7[" + fPrio.getColored() + "§7]" : "";
player.sendMessage(plugin.lang().format("ticket.created", "{id}", String.valueOf(id)) + catInfo + prioInfo);
plugin.getTicketManager().notifyTeam(ticket);
});
});
}
// ── /ticket list ──────────────────────────────────────────────────────
private void handleList(Player player) {
if (player.hasPermission("ticket.support") || player.hasPermission("ticket.admin")) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
Bukkit.getScheduler().runTask(plugin, () -> plugin.getTicketGUI().openGUI(player)));
} else {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
Bukkit.getScheduler().runTask(plugin, () -> plugin.getTicketGUI().openPlayerGUI(player)));
}
}
// ── /ticket claim ─────────────────────────────────────────────────────
private void handleClaim(Player player, String[] args) {
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("claim.usage")); return; }
int id;
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { plugin.lang().send(player, "general.invalid-id"); return; }
final int ticketId = id;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().claimTicket(ticketId, player.getUniqueId(), player.getName());
Bukkit.getScheduler().runTask(plugin, () -> {
if (!ok) { plugin.lang().send(player, "general.already-claimed"); return; }
Ticket t = getCachedOrFetch(ticketId);
if (t == null) return;
player.sendMessage(plugin.lang().format("ticket.claimed",
"{id}", String.valueOf(ticketId), "{player}", t.getCreatorName()));
plugin.getTicketManager().notifyCreatorClaimed(t);
plugin.getTicketCache().invalidate(ticketId);
});
});
}
// ── /ticket close ─────────────────────────────────────────────────────
private void handleClose(Player player, String[] args) {
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("close.usage")); return; }
int id;
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { plugin.lang().send(player, "general.invalid-id"); return; }
final int ticketId = id;
final String comment = args.length > 2
? String.join(" ", Arrays.copyOfRange(args, 2, args.length)) : "";
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().closeTicket(ticketId, comment);
if (ok) {
Ticket t = getCachedOrFetch(ticketId);
if (t != null) plugin.getDatabaseManager().recordClosedTicket(t, player.getName());
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.lang().format("ticket.closed", "{id}", String.valueOf(ticketId)));
if (t != null) {
t.setCloseComment(comment);
plugin.getTicketManager().notifyCreatorClosed(t, player.getName());
}
});
} else {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "general.ticket-not-found"));
}
});
}
// ── /ticket forward ───────────────────────────────────────────────────
private void handleForward(Player player, String[] args) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 3) { player.sendMessage(plugin.lang().get("forward.usage")); return; }
int id;
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { plugin.lang().send(player, "general.invalid-id"); return; }
Player target = Bukkit.getPlayer(args[2]);
if (target == null) {
player.sendMessage(plugin.isBungeeCordEnabled()
? plugin.lang().format("forward.bungee-offline", "{player}", args[2])
: plugin.lang().get("forward.local-not-found"));
return;
}
final int ticketId = id;
final String fromName = player.getName();
final Player t = target;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().forwardTicket(ticketId, t.getUniqueId(), t.getName());
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (!ok) { plugin.lang().send(player, "general.ticket-not-found"); return; }
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) return;
player.sendMessage(plugin.lang().format("ticket.forwarded",
"{id}", String.valueOf(ticketId), "{player}", t.getName()));
plugin.getTicketManager().notifyForwardedTo(ticket, fromName);
plugin.getTicketManager().notifyCreatorForwarded(ticket);
});
});
}
// ── /ticket comment ───────────────────────────────────────────────────
private void handleComment(Player player, String[] args) {
if (!player.hasPermission("ticket.create") && !player.hasPermission("ticket.support")
&& !player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 3) { player.sendMessage(plugin.lang().get("comment.usage")); return; }
int id;
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { plugin.lang().send(player, "general.invalid-id"); return; }
String msg = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
if (msg.length() > 500) { player.sendMessage(plugin.lang().get("comment.too-long")); return; }
final int ticketId = id;
final String message = msg;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "general.ticket-not-found"));
return;
}
boolean isOwner = ticket.getCreatorUUID().equals(player.getUniqueId());
boolean isStaff = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
if (!isOwner && !isStaff) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "comment.no-permission"));
return;
}
TicketComment comment = new TicketComment(ticketId, player.getUniqueId(), player.getName(), message);
boolean ok = plugin.getDatabaseManager().addComment(comment);
Bukkit.getScheduler().runTask(plugin, () -> {
if (ok) {
player.sendMessage(plugin.lang().format("comment.saved", "{id}", String.valueOf(ticketId)));
notifyCommentReceivers(player, ticket, message);
} else {
player.sendMessage(plugin.lang().get("comment.error"));
}
});
});
}
private void notifyCommentReceivers(Player author, Ticket ticket, String message) {
String onlineMsg = plugin.lang().format("comment.notify-online",
"{id}", String.valueOf(ticket.getId()), "{author}", author.getName(), "{message}", message);
String offlineMsg = plugin.lang().format("comment.notify-offline",
"{id}", String.valueOf(ticket.getId()), "{author}", author.getName(), "{message}", message);
if (!ticket.getCreatorUUID().equals(author.getUniqueId())) {
Player creator = Bukkit.getPlayer(ticket.getCreatorUUID());
if (creator != null && creator.isOnline()) {
creator.sendMessage(onlineMsg);
} else if (plugin.isBungeeCordEnabled()) {
plugin.getBungeeMessenger().sendMessageToPlayer(
ticket.getCreatorUUID(), ticket.getCreatorName(), onlineMsg);
} else {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
plugin.getDatabaseManager().addPendingNotification(ticket.getCreatorUUID(), offlineMsg));
}
}
if (!author.hasPermission("ticket.support") && !author.hasPermission("ticket.admin")) {
if (plugin.isBungeeCordEnabled()) {
plugin.getBungeeMessenger().broadcastTeamNotification(onlineMsg);
} else {
var 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 claimerMsg = plugin.lang().format("comment.claimer-offline",
"{id}", String.valueOf(ticket.getId()),
"{author}", author.getName(), "{message}", message);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
plugin.getDatabaseManager().addPendingNotification(claimerUUID, claimerMsg));
}
}
for (Player p : Bukkit.getOnlinePlayers()) {
if (!p.equals(author) && (p.hasPermission("ticket.support") || p.hasPermission("ticket.admin")))
p.sendMessage(onlineMsg);
}
}
}
}
// ── /ticket rate ──────────────────────────────────────────────────────
private void handleRate(Player player, String[] args) {
if (!plugin.getConfig().getBoolean("rating-enabled", true)) {
plugin.lang().send(player, "rating.disabled"); return;
}
if (args.length < 3) { player.sendMessage(plugin.lang().get("rating.usage")); return; }
int id;
try { id = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { plugin.lang().send(player, "general.invalid-id"); return; }
String raw = args[2].toLowerCase();
String rating;
if (raw.equals("good") || raw.equals("gut") || raw.equals("thumbsup")) rating = "THUMBS_UP";
else if (raw.equals("bad") || raw.equals("schlecht") || raw.equals("thumbsdown")) rating = "THUMBS_DOWN";
else { player.sendMessage(plugin.lang().get("rating.invalid")); return; }
final int ticketId = id;
final String finalRating = rating;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
Ticket ticket = getCachedOrFetch(ticketId);
if (ticket == null) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "general.ticket-not-found")); return;
}
if (!ticket.getCreatorUUID().equals(player.getUniqueId())) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "rating.not-yours")); return;
}
if (ticket.hasRating()) {
Bukkit.getScheduler().runTask(plugin, () -> plugin.lang().send(player, "rating.already-rated")); return;
}
boolean ok = plugin.getDatabaseManager().rateTicket(ticketId, finalRating);
plugin.getTicketCache().invalidate(ticketId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (ok) player.sendMessage("THUMBS_UP".equals(finalRating)
? plugin.lang().get("rating.saved-good")
: plugin.lang().get("rating.saved-bad"));
else player.sendMessage(plugin.lang().get("rating.not-closeable"));
});
});
}
// ── /ticket blacklist ─────────────────────────────────────────────────
private void handleBlacklist(Player player, String[] args) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("blacklist.usage")); return; }
switch (args[1].toLowerCase()) {
case "add", "hinzufügen", "hinzufuegen" -> {
if (args.length < 3) { player.sendMessage(plugin.lang().get("blacklist.usage-add")); return; }
String targetName = args[2];
String reason = args.length > 3
? String.join(" ", Arrays.copyOfRange(args, 3, args.length)) : "Missbrauch";
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
if (target.getUniqueId() == null) { plugin.lang().send(player, "general.player-not-found"); return; }
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().addBlacklist(
target.getUniqueId(), targetName, reason, player.getName());
Bukkit.getScheduler().runTask(plugin, () -> {
if (ok) player.sendMessage(plugin.lang().format("blacklist.added",
"{player}", targetName, "{reason}", reason));
else player.sendMessage(plugin.lang().get("blacklist.already"));
});
});
}
case "remove", "entfernen" -> {
if (args.length < 3) { player.sendMessage(plugin.lang().get("blacklist.usage-remove")); return; }
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(args[2]);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().removeBlacklist(target.getUniqueId());
Bukkit.getScheduler().runTask(plugin, () -> {
if (ok) player.sendMessage(plugin.lang().format("blacklist.removed", "{player}", args[2]));
else player.sendMessage(plugin.lang().get("blacklist.not-found"));
});
});
}
case "list", "liste" -> {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
var list = plugin.getDatabaseManager().getBlacklist();
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().format("blacklist.list-header",
"{count}", String.valueOf(list.size())));
player.sendMessage(plugin.lang().get("general.separator"));
if (list.isEmpty()) {
player.sendMessage(plugin.lang().get("blacklist.list-empty"));
} else {
for (String[] e : list)
player.sendMessage(plugin.lang().format("blacklist.list-entry",
"{player}", e[1], "{reason}", e[2], "{by}", e[3]));
}
player.sendMessage(plugin.lang().get("general.separator"));
});
});
}
default -> player.sendMessage(plugin.lang().get("blacklist.usage"));
}
}
// ── /ticket top ───────────────────────────────────────────────────────
private void handleTop(Player player) {
if (!player.hasPermission("ticket.create") && !player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
List<String[]> top = plugin.getDatabaseManager().getTopCreators(5);
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("top.header"));
player.sendMessage(plugin.lang().get("general.separator"));
if (top.isEmpty()) {
player.sendMessage(plugin.lang().get("top.empty"));
} else {
String[] medals = {"&e🥇", "&7🥈", "&6🥉", "&7#4", "&7#5"};
for (String[] entry : top) {
int rankIdx = Integer.parseInt(entry[0]) - 1;
String medal = plugin.lang().color(rankIdx < medals.length ? medals[rankIdx] : "&7#" + entry[0]);
int cnt = Integer.parseInt(entry[2]);
String label = cnt == 1
? plugin.lang().get("stats.top-ticket-label")
: plugin.lang().get("stats.top-tickets-label");
player.sendMessage(plugin.lang().format("top.entry",
"{medal}", medal,
"{name}", String.format("%-16s", entry[1]),
"{count}", entry[2],
"{label}", label));
}
}
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("top.footer"));
});
});
}
// ── /ticket reload ────────────────────────────────────────────────────
private void handleReload(Player player) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
// Reihenfolge zwingend:
// 1. Config neu laden (liest language neu ein)
// 2. lang().reload() (liest language aus der frischen Config, baut cmdNames neu)
// 3. GUI reloadConfig() (liest Rows/Slots aus der frischen Config)
// 4. weitere Manager (nutzen ggf. frische lang-Texte)
plugin.reloadConfig();
plugin.lang().reload();
plugin.reloadSettings(); // serverName & debug-Flag aktualisieren
plugin.getTicketGUI().reloadConfig(); // Slots, Rows & Materialien neu laden
plugin.getTicketGUI().reloadTitles(); // Inventar-Titel aktualisieren
plugin.getFaqGUI().reloadConfig(); // FAQ-Slots, Rows & Materialien neu laden
plugin.getCategoryManager().reload();
plugin.getFaqManager().reload();
plugin.getTicketCache().clear();
player.sendMessage(plugin.lang().get("reload.success"));
if (plugin.isBungeeCordEnabled())
player.sendMessage(plugin.lang().format("reload.bungee-info", "{server}", plugin.getServerName()));
}
// ── /ticket archive ───────────────────────────────────────────────────
private void handleArchive(Player player) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
int count = plugin.getDatabaseManager().archiveClosedTickets();
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.lang().format("system.archive-success", "{count}", String.valueOf(count)));
else player.sendMessage(plugin.lang().get("system.archive-fail"));
});
});
}
// ── /ticket stats ─────────────────────────────────────────────────────
private void handleStats(Player player) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
var stats = plugin.getDatabaseManager().getTicketStats();
var staffRatings = plugin.getDatabaseManager().getStaffRatings();
var topCreators = plugin.getDatabaseManager().getTopCreators(5);
Bukkit.getScheduler().runTask(plugin, () -> {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("stats.header"));
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().format("stats.total", "{count}", String.valueOf(stats.total)));
player.sendMessage(plugin.lang().format("stats.open", "{count}", String.valueOf(stats.open)));
player.sendMessage(plugin.lang().format("stats.closed", "{count}", String.valueOf(stats.closed)));
player.sendMessage(plugin.lang().format("stats.forwarded", "{count}", String.valueOf(stats.forwarded)));
if (plugin.getConfig().getBoolean("rating-enabled", true)) {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("stats.ratings-header"));
player.sendMessage(plugin.lang().format("stats.ratings-summary",
"{up}", String.valueOf(stats.thumbsUp), "{down}", String.valueOf(stats.thumbsDown)));
int totalRated = stats.thumbsUp + stats.thumbsDown;
if (totalRated > 0) {
int pct = (int) Math.round(stats.thumbsUp * 100.0 / totalRated);
player.sendMessage(plugin.lang().format("stats.ratings-percent", "{percent}", String.valueOf(pct)));
}
if (!staffRatings.isEmpty()) {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("stats.staff-header"));
player.sendMessage(plugin.lang().get("stats.staff-table-header"));
for (String[] row : staffRatings)
player.sendMessage(plugin.lang().format("stats.staff-entry",
"{name}", String.format("%-16s", row[0]),
"{up}", String.format("%-5s", row[1]),
"{down}", String.format("%-5s", row[2]),
"{total}", String.format("%-8s", row[3]),
"{percent}", row[4]));
}
}
if (plugin.isBungeeCordEnabled() && !stats.byServer.isEmpty()) {
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("stats.servers-header"));
stats.byServer.entrySet().stream()
.sorted((a, b) -> b.getValue() - a.getValue())
.forEach(e -> player.sendMessage(plugin.lang().format("stats.server-entry",
"{server}", e.getKey(), "{count}", String.valueOf(e.getValue()))));
}
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().get("stats.top-header"));
if (topCreators.isEmpty()) {
player.sendMessage(plugin.lang().get("stats.top-empty"));
} else {
String[] medals = {"&e🥇", "&7🥈", "&6🥉", "&7#4", "&7#5"};
for (String[] entry : topCreators) {
int ri = Integer.parseInt(entry[0]) - 1;
String medal = plugin.lang().color(ri < medals.length ? medals[ri] : "&7#" + entry[0]);
int cnt = Integer.parseInt(entry[2]);
String label = cnt == 1 ? plugin.lang().get("stats.top-ticket-label")
: plugin.lang().get("stats.top-tickets-label");
player.sendMessage(plugin.lang().format("stats.top-entry",
"{medal}", medal, "{name}", String.format("%-16s", entry[1]),
"{count}", entry[2], "{label}", label));
}
}
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().format("stats.cache-info",
"{count}", String.valueOf(plugin.getTicketCache().size())));
});
});
}
// ── /ticket migrate ───────────────────────────────────────────────────
private void handleMigrate(Player player, String[] args) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("migrate.usage")); return; }
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
String mode = args[1].toLowerCase();
int migrated = 0;
if (mode.equals("tomysql")) migrated = plugin.getDatabaseManager().migrateToMySQL();
else if (mode.equals("tofile")) migrated = plugin.getDatabaseManager().migrateToFile();
else {
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.lang().get("system.unknown-mode"))); return;
}
int f = migrated;
Bukkit.getScheduler().runTask(plugin, () -> {
if (f > 0) player.sendMessage(plugin.lang().format("system.migration-success", "{count}", String.valueOf(f)));
else player.sendMessage(plugin.lang().get("system.migration-fail"));
});
});
}
// ── /ticket export ────────────────────────────────────────────────────
private void handleExport(Player player, String[] args) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("export.usage")); return; }
String filename = args[1];
File exportFile = new File(plugin.getDataFolder(), filename);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
int count = plugin.getDatabaseManager().exportTickets(exportFile);
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.lang().format("system.export-success",
"{count}", String.valueOf(count), "{file}", filename));
else player.sendMessage(plugin.lang().get("system.export-fail"));
});
});
}
// ── /ticket import ────────────────────────────────────────────────────
private void handleImport(Player player, String[] args) {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 2) { player.sendMessage(plugin.lang().get("import.usage")); return; }
String filename = args[1];
File importFile = new File(plugin.getDataFolder(), filename);
if (!importFile.exists()) {
player.sendMessage(plugin.lang().format("system.file-not-found", "{file}", filename)); return;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
int count = plugin.getDatabaseManager().importTickets(importFile);
Bukkit.getScheduler().runTask(plugin, () -> {
if (count > 0) player.sendMessage(plugin.lang().format("system.import-success", "{count}", String.valueOf(count)));
else player.sendMessage(plugin.lang().get("system.import-fail"));
});
});
}
// ── /ticket setpriority ───────────────────────────────────────────────
private void handleSetPriority(Player player, String[] args) {
if (!player.hasPermission("ticket.support") && !player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (!plugin.getConfig().getBoolean("priorities-enabled", true)) {
player.sendMessage(plugin.lang().get("setpriority.disabled")); return;
}
if (args.length < 3) { player.sendMessage(plugin.lang().get("setpriority.usage")); return; }
int ticketId;
try { ticketId = Integer.parseInt(args[1]); }
catch (NumberFormatException e) {
player.sendMessage(plugin.lang().format("general.invalid-player-id", "{id}", args[1])); return;
}
TicketPriority prio = parsePriority(args[2]);
if (prio == null) { player.sendMessage(plugin.lang().get("setpriority.invalid")); return; }
final int finalId = ticketId;
final TicketPriority finalPrio = prio;
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
boolean ok = plugin.getDatabaseManager().setTicketPriority(finalId, finalPrio);
plugin.getTicketCache().invalidate(finalId);
Bukkit.getScheduler().runTask(plugin, () -> {
if (ok) player.sendMessage(plugin.lang().format("setpriority.success",
"{id}", String.valueOf(finalId), "{priority}", finalPrio.getColored()));
else player.sendMessage(plugin.lang().format("setpriority.not-found",
"{id}", String.valueOf(finalId)));
});
});
}
// ── /ticket faq ───────────────────────────────────────────────────────
private void handleFaq(Player player, String[] args) {
if (args.length == 1) {
plugin.getFaqGUI().openFaqGUI(player);
return;
}
switch (args[1].toLowerCase()) {
case "add", "hinzufügen", "hinzufuegen" -> {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 3) {
player.sendMessage(plugin.lang().get("faq.usage-add"));
player.sendMessage(plugin.lang().get("faq.usage-add-example")); 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.lang().get("faq.separator-missing"));
player.sendMessage(plugin.lang().get("faq.separator-example")); return;
}
FaqEntry created = plugin.getFaqManager().add(parts[0].trim(), parts[1].trim());
player.sendMessage(plugin.lang().format("faq.created", "{id}", String.valueOf(created.getId())));
player.sendMessage(plugin.lang().format("faq.created-question", "{question}", created.getQuestion()));
player.sendMessage(plugin.lang().format("faq.created-answer", "{answer}", created.getAnswer()));
}
case "edit", "bearbeiten" -> {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 4) { player.sendMessage(plugin.lang().get("faq.usage-edit")); return; }
int id;
try { id = Integer.parseInt(args[2]); }
catch (NumberFormatException e) {
player.sendMessage(plugin.lang().format("faq.invalid-id", "{id}", 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.lang().get("faq.separator-short")); return;
}
boolean ok = plugin.getFaqManager().edit(id, parts[0].trim(), parts[1].trim());
player.sendMessage(ok
? plugin.lang().format("faq.updated", "{id}", String.valueOf(id))
: plugin.lang().format("faq.not-found", "{id}", String.valueOf(id)));
}
case "delete", "remove", "löschen", "loeschen", "entfernen" -> {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
if (args.length < 3) { player.sendMessage(plugin.lang().get("faq.usage-delete")); return; }
int id;
try { id = Integer.parseInt(args[2]); }
catch (NumberFormatException e) {
player.sendMessage(plugin.lang().format("faq.invalid-id", "{id}", args[2])); return;
}
boolean ok = plugin.getFaqManager().delete(id);
player.sendMessage(ok
? plugin.lang().format("faq.deleted", "{id}", String.valueOf(id))
: plugin.lang().format("faq.not-found", "{id}", String.valueOf(id)));
}
case "reload", "neuladen" -> {
if (!player.hasPermission("ticket.admin")) {
plugin.lang().send(player, "general.no-permission"); return;
}
plugin.getFaqManager().reload();
player.sendMessage(plugin.lang().format("faq.reloaded",
"{count}", String.valueOf(plugin.getFaqManager().getAll().size())));
}
case "list", "liste" -> {
List<FaqEntry> all = plugin.getFaqManager().getAll();
player.sendMessage(plugin.lang().get("general.separator"));
player.sendMessage(plugin.lang().format("faq.list-header", "{count}", String.valueOf(all.size())));
player.sendMessage(plugin.lang().get("general.separator"));
if (all.isEmpty()) {
player.sendMessage(plugin.lang().get("faq.list-empty"));
} else {
for (FaqEntry e : all) {
player.sendMessage(plugin.lang().format("faq.list-entry",
"{id}", String.valueOf(e.getId()), "{question}", e.getQuestion()));
player.sendMessage(plugin.lang().format("faq.list-answer", "{answer}", e.getAnswer()));
}
}
player.sendMessage(plugin.lang().get("general.separator"));
if (player.hasPermission("ticket.admin"))
player.sendMessage(plugin.lang().get("faq.list-admin-hint"));
}
default -> {
player.sendMessage(plugin.lang().get("faq.unknown-sub"));
player.sendMessage(plugin.lang().get("faq.hint-open"));
if (player.hasPermission("ticket.admin"))
player.sendMessage(plugin.lang().get("faq.admin-commands"));
}
}
}
// ── Hilfsmethoden ─────────────────────────────────────────────────────
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()) {
case "low", "niedrig" -> TicketPriority.LOW;
case "normal" -> TicketPriority.NORMAL;
case "high", "hoch" -> TicketPriority.HIGH;
case "urgent", "dringend" -> TicketPriority.URGENT;
default -> null;
};
}
// ── Tab-Completion ────────────────────────────────────────────────────
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
List<String> completions = new ArrayList<>();
if (!(sender instanceof Player player)) return completions;
// Immer direkt vom LanguageManager lesen kein Cache, immer aktuell nach reload
final boolean useDe = plugin.lang().acceptsGerman();
final boolean useEn = plugin.lang().acceptsEnglish();
if (args.length == 1) {
List<String> subs = new ArrayList<>();
if (useEn) subs.addAll(List.of("create", "list", "comment", "top", "faq"));
if (useDe) subs.addAll(List.of("erstellen", "liste", "kommentar", "top", "faq"));
if (player.hasPermission("ticket.support") || player.hasPermission("ticket.admin")) {
if (useEn) subs.addAll(List.of("claim", "close"));
if (useDe) subs.addAll(List.of("übernehmen", "schließen"));
}
if (plugin.getConfig().getBoolean("rating-enabled", true)) {
if (useEn) subs.add("rate");
if (useDe) subs.add("bewerten");
}
if (player.hasPermission("ticket.admin")) {
if (useEn) subs.addAll(List.of("forward", "reload", "stats", "archive",
"migrate", "export", "import", "blacklist"));
if (useDe) subs.addAll(List.of("weiterleiten", "neuladen", "statistik",
"archivieren", "migrieren", "exportieren", "importieren", "sperrliste"));
}
if ((player.hasPermission("ticket.support") || player.hasPermission("ticket.admin"))
&& plugin.getConfig().getBoolean("priorities-enabled", true)) {
if (useEn) subs.add("setpriority");
if (useDe) subs.add("priorität");
}
for (String s : subs)
if (s.startsWith(args[0].toLowerCase())) completions.add(s);
} else if (args.length == 2 && normalize(args[0]).equals("faq")) {
List<String> faqSubs = new ArrayList<>();
if (useEn) faqSubs.add("list");
if (useDe) faqSubs.add("liste");
if (player.hasPermission("ticket.admin")) {
if (useEn) faqSubs.addAll(List.of("add", "edit", "delete", "reload"));
if (useDe) faqSubs.addAll(List.of("hinzufügen", "bearbeiten", "löschen", "neuladen"));
}
for (String s : faqSubs)
if (s.startsWith(args[1].toLowerCase())) completions.add(s);
} else if (args.length == 3 && normalize(args[0]).equals("faq")
&& (args[1].equalsIgnoreCase("edit") || args[1].equalsIgnoreCase("delete")
|| args[1].equalsIgnoreCase("bearbeiten") || args[1].equalsIgnoreCase("löschen"))
&& player.hasPermission("ticket.admin")) {
for (FaqEntry e : plugin.getFaqManager().getAll())
completions.add(String.valueOf(e.getId()));
} else if (args.length == 2 && normalize(args[0]).equals("create")
&& plugin.getConfig().getBoolean("categories-enabled", true)) {
for (ConfigCategory c : plugin.getCategoryManager().getAll())
if (c.getKey().startsWith(args[1].toLowerCase())) completions.add(c.getKey());
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 && normalize(args[0]).equals("create")
&& plugin.getConfig().getBoolean("priorities-enabled", true)) {
for (String p : List.of("low", "normal", "high", "urgent"))
if (p.startsWith(args[2].toLowerCase())) completions.add(p);
} else if (args.length == 3 && normalize(args[0]).equals("setpriority")) {
for (String p : List.of("low", "normal", "high", "urgent"))
if (p.startsWith(args[2].toLowerCase())) completions.add(p);
} else if (args.length == 3 && normalize(args[0]).equals("forward")) {
for (Player p : Bukkit.getOnlinePlayers())
if (p.getName().toLowerCase().startsWith(args[2].toLowerCase())) completions.add(p.getName());
} else if (args.length == 2 && normalize(args[0]).equals("blacklist")) {
if (useEn) completions.addAll(List.of("add", "remove", "list"));
if (useDe) completions.addAll(List.of("hinzufügen", "entfernen", "liste"));
} else if (args.length == 3 && normalize(args[0]).equals("blacklist")
&& (args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("remove")
|| args[1].equalsIgnoreCase("hinzufügen") || args[1].equalsIgnoreCase("entfernen"))) {
for (Player p : Bukkit.getOnlinePlayers())
if (p.getName().toLowerCase().startsWith(args[2].toLowerCase())) completions.add(p.getName());
} else if (args.length == 3 && normalize(args[0]).equals("rate")) {
if (useEn) completions.addAll(List.of("good", "bad"));
if (useDe) completions.addAll(List.of("gut", "schlecht"));
}
return completions;
}
}