Add /lmutelist and /lmutehistory commands
- /lmutelist - shows all active mutes with remaining time - /lmutehistory <player> - shows mute history for a player - Added mute history tracking (mute-history.json) - History includes: who muted, when, duration, reason, unmute info - Permissions: lochat.mutelist, lochat.mutehistory
This commit is contained in:
parent
c6175d9d61
commit
d92799613e
7 changed files with 428 additions and 10 deletions
|
|
@ -120,6 +120,12 @@ public final class LoChat extends JavaPlugin {
|
|||
UnmuteCommand unmuteCommand = new UnmuteCommand(this);
|
||||
getCommand("lunmute").setExecutor(unmuteCommand);
|
||||
getCommand("lunmute").setTabCompleter(unmuteCommand);
|
||||
|
||||
getCommand("lmutelist").setExecutor(new MuteListCommand(this));
|
||||
|
||||
MuteHistoryCommand muteHistoryCommand = new MuteHistoryCommand(this);
|
||||
getCommand("lmutehistory").setExecutor(muteHistoryCommand);
|
||||
getCommand("lmutehistory").setTabCompleter(muteHistoryCommand);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
// Мутим оффлайн игрока
|
||||
plugin.getMuteManager().mute(offlinePlayer.getUniqueId(), duration, reason, sender.getName());
|
||||
String offlineName = offlinePlayer.getName() != null ? offlinePlayer.getName() : targetName;
|
||||
plugin.getMuteManager().mute(offlinePlayer.getUniqueId(), offlineName, duration, reason, sender.getName());
|
||||
|
||||
String timeDisplay = duration == 0 ? "навсегда" : plugin.getMuteManager().formatTime(duration);
|
||||
sender.sendMessage("§aИгрок §e" + targetName + " §aзамучен на §e" + timeDisplay);
|
||||
|
|
@ -102,7 +103,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
// Мутим игрока
|
||||
plugin.getMuteManager().mute(target.getUniqueId(), duration, reason, sender.getName());
|
||||
plugin.getMuteManager().mute(target.getUniqueId(), target.getName(), duration, reason, sender.getName());
|
||||
|
||||
String timeDisplay = duration == 0 ? "навсегда" : plugin.getMuteManager().formatTime(duration);
|
||||
|
||||
|
|
|
|||
142
src/main/java/com/loki/lochat/commands/MuteHistoryCommand.java
Normal file
142
src/main/java/com/loki/lochat/commands/MuteHistoryCommand.java
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.managers.MuteManager;
|
||||
import org.bukkit.Bukkit;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /lmutehistory [ник] - история мутов игрока
|
||||
*/
|
||||
public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteHistoryCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.mutehistory")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§cИспользование: /lmutehistory <ник>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String targetName = args[0];
|
||||
UUID targetUUID = null;
|
||||
|
||||
// Ищем игрока
|
||||
Player target = Bukkit.getPlayer(targetName);
|
||||
if (target != null) {
|
||||
targetUUID = target.getUniqueId();
|
||||
} else {
|
||||
// Пробуем найти оффлайн игрока
|
||||
@SuppressWarnings("deprecation")
|
||||
var offlinePlayer = Bukkit.getOfflinePlayer(targetName);
|
||||
if (offlinePlayer.hasPlayedBefore() || offlinePlayer.isOnline()) {
|
||||
targetUUID = offlinePlayer.getUniqueId();
|
||||
} else {
|
||||
// Пробуем найти по имени в истории
|
||||
targetUUID = plugin.getMuteManager().getUUIDByName(targetName);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetUUID == null) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found"));
|
||||
return true;
|
||||
}
|
||||
|
||||
List<MuteManager.MuteHistoryEntry> history = plugin.getMuteManager().getPlayerHistory(targetUUID);
|
||||
|
||||
if (history.isEmpty()) {
|
||||
sender.sendMessage("§aУ игрока §e" + targetName + " §aнет истории мутов");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage("§6§l=== История мутов " + targetName + " (" + history.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
// Выводим от новых к старым
|
||||
for (int i = history.size() - 1; i >= 0; i--) {
|
||||
MuteManager.MuteHistoryEntry entry = history.get(i);
|
||||
|
||||
// Статус
|
||||
String status;
|
||||
if (entry.unmuted) {
|
||||
status = "§aРазмучен";
|
||||
} else {
|
||||
// Проверяем, активен ли ещё мут
|
||||
if (entry.isPermanent()) {
|
||||
status = "§cАктивен (навсегда)";
|
||||
} else {
|
||||
long endTime = entry.mutedAt + entry.duration;
|
||||
if (System.currentTimeMillis() > endTime) {
|
||||
status = "§7Истёк";
|
||||
} else {
|
||||
long remaining = endTime - System.currentTimeMillis();
|
||||
status = "§cАктивен (" + plugin.getMuteManager().formatTime(remaining) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Длительность
|
||||
String durationStr = entry.isPermanent() ? "Навсегда" : plugin.getMuteManager().formatTime(entry.duration);
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + dateFormat.format(new Date(entry.mutedAt)) + " §7- " + status);
|
||||
sender.sendMessage(" §7Длительность: §f" + durationStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (entry.reason != null ? entry.reason : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + entry.mutedBy);
|
||||
|
||||
if (entry.unmuted && entry.unmutedBy != null) {
|
||||
sender.sendMessage(" §7Размутил: §f" + entry.unmutedBy + " §7(" + dateFormat.format(new Date(entry.unmutedAt)) + ")");
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
// Ограничиваем вывод
|
||||
if (index > 10) {
|
||||
sender.sendMessage("§7... и ещё " + (history.size() - 10) + " записей");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
String prefix = args[0].toLowerCase();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.getName().toLowerCase().startsWith(prefix)) {
|
||||
completions.add(player.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
76
src/main/java/com/loki/lochat/commands/MuteListCommand.java
Normal file
76
src/main/java/com/loki/lochat/commands/MuteListCommand.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.managers.MuteManager;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /lmutelist - список активных мутов
|
||||
*/
|
||||
public class MuteListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.mutelist")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<UUID, MuteManager.MuteData> activeMutes = plugin.getMuteManager().getActiveMutes();
|
||||
|
||||
if (activeMutes.isEmpty()) {
|
||||
sender.sendMessage("§aНет активных мутов");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage("§6§l=== Активные муты (" + activeMutes.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
for (Map.Entry<UUID, MuteManager.MuteData> entry : activeMutes.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
MuteManager.MuteData data = entry.getValue();
|
||||
|
||||
// Получаем имя игрока
|
||||
String playerName = data.playerName;
|
||||
if (playerName == null) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
playerName = offlinePlayer.getName() != null ? offlinePlayer.getName() : uuid.toString().substring(0, 8);
|
||||
}
|
||||
|
||||
// Форматируем время
|
||||
String timeStr;
|
||||
if (data.isPermanent()) {
|
||||
timeStr = "§4Навсегда";
|
||||
} else {
|
||||
long remaining = data.endTime - System.currentTimeMillis();
|
||||
timeStr = "§e" + plugin.getMuteManager().formatTime(remaining);
|
||||
}
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + timeStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (data.reason != null ? data.reason : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + data.mutedBy + " §7(" + dateFormat.format(new Date(data.mutedAt)) + ")");
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
// Размучиваем оффлайн игрока
|
||||
if (plugin.getMuteManager().unmute(offlinePlayer.getUniqueId())) {
|
||||
if (plugin.getMuteManager().unmute(offlinePlayer.getUniqueId(), sender.getName())) {
|
||||
sender.sendMessage("§aИгрок §e" + targetName + " §aразмучен");
|
||||
|
||||
if (!silent) {
|
||||
|
|
@ -66,7 +66,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
// Размучиваем онлайн игрока
|
||||
if (plugin.getMuteManager().unmute(target.getUniqueId())) {
|
||||
if (plugin.getMuteManager().unmute(target.getUniqueId(), sender.getName())) {
|
||||
sender.sendMessage("§aИгрок §e" + target.getName() + " §aразмучен");
|
||||
target.sendMessage("§aВы были размучены!");
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import com.loki.lochat.LoChat;
|
|||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
|
@ -18,42 +17,69 @@ public class MuteManager {
|
|||
|
||||
private final LoChat plugin;
|
||||
private final Map<UUID, MuteData> mutes = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, List<MuteHistoryEntry>> history = new ConcurrentHashMap<>();
|
||||
private final File dataFile;
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public MuteManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
|
||||
this.historyFile = new File(plugin.getDataFolder(), "mute-history.json");
|
||||
load();
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Замутить игрока
|
||||
* @param uuid UUID игрока
|
||||
* @param playerName имя игрока
|
||||
* @param duration длительность в миллисекундах (0 = перманентный)
|
||||
* @param reason причина
|
||||
* @param mutedBy кто замутил
|
||||
*/
|
||||
public void mute(UUID uuid, long duration, String reason, String mutedBy) {
|
||||
public void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0;
|
||||
mutes.put(uuid, new MuteData(endTime, reason, mutedBy, System.currentTimeMillis()));
|
||||
MuteData data = new MuteData(endTime, reason, mutedBy, System.currentTimeMillis(), playerName);
|
||||
mutes.put(uuid, data);
|
||||
|
||||
// Добавляем в историю
|
||||
addToHistory(uuid, playerName, duration, reason, mutedBy);
|
||||
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Замутить игрока (без имени - для совместимости)
|
||||
*/
|
||||
public void mute(UUID uuid, long duration, String reason, String mutedBy) {
|
||||
mute(uuid, null, duration, reason, mutedBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Размутить игрока
|
||||
* @param uuid UUID игрока
|
||||
* @param unmutedBy кто размутил (null если автоматически)
|
||||
* @return true если игрок был замучен
|
||||
*/
|
||||
public boolean unmute(UUID uuid) {
|
||||
public boolean unmute(UUID uuid, String unmutedBy) {
|
||||
MuteData removed = mutes.remove(uuid);
|
||||
if (removed != null) {
|
||||
// Обновляем историю - помечаем как размученный
|
||||
updateHistoryUnmute(uuid, unmutedBy);
|
||||
saveAsync();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Размутить игрока (для совместимости)
|
||||
*/
|
||||
public boolean unmute(UUID uuid) {
|
||||
return unmute(uuid, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить, замучен ли игрок
|
||||
*/
|
||||
|
|
@ -172,8 +198,121 @@ public class MuteManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadHistory() {
|
||||
if (!historyFile.exists()) return;
|
||||
|
||||
try (Reader reader = new FileReader(historyFile)) {
|
||||
Type type = new TypeToken<Map<String, List<MuteHistoryEntry>>>(){}.getType();
|
||||
Map<String, List<MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
history.put(UUID.fromString(key), new ArrayList<>(value));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveHistory() {
|
||||
try {
|
||||
if (!historyFile.getParentFile().exists()) {
|
||||
historyFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Map<String, List<MuteHistoryEntry>> toSave = new ConcurrentHashMap<>();
|
||||
history.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(historyFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHistoryAsync() {
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, this::saveHistory);
|
||||
}
|
||||
|
||||
private void saveAsync() {
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, this::save);
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
save();
|
||||
saveHistory();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавить запись в историю
|
||||
*/
|
||||
private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
List<MuteHistoryEntry> playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
playerHistory.add(new MuteHistoryEntry(
|
||||
playerName,
|
||||
duration,
|
||||
reason,
|
||||
mutedBy,
|
||||
System.currentTimeMillis(),
|
||||
false,
|
||||
null,
|
||||
0
|
||||
));
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить историю при размуте
|
||||
*/
|
||||
private void updateHistoryUnmute(UUID uuid, String unmutedBy) {
|
||||
List<MuteHistoryEntry> playerHistory = history.get(uuid);
|
||||
if (playerHistory != null && !playerHistory.isEmpty()) {
|
||||
// Находим последний активный мут
|
||||
for (int i = playerHistory.size() - 1; i >= 0; i--) {
|
||||
MuteHistoryEntry entry = playerHistory.get(i);
|
||||
if (!entry.unmuted) {
|
||||
entry.unmuted = true;
|
||||
entry.unmutedBy = unmutedBy;
|
||||
entry.unmutedAt = System.currentTimeMillis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить список всех активных мутов
|
||||
*/
|
||||
public Map<UUID, MuteData> getActiveMutes() {
|
||||
// Очищаем истёкшие муты
|
||||
mutes.entrySet().removeIf(entry -> {
|
||||
MuteData data = entry.getValue();
|
||||
return data.endTime > 0 && System.currentTimeMillis() > data.endTime;
|
||||
});
|
||||
return new HashMap<>(mutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить историю мутов игрока
|
||||
*/
|
||||
public List<MuteHistoryEntry> getPlayerHistory(UUID uuid) {
|
||||
return history.getOrDefault(uuid, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить UUID по имени из истории
|
||||
*/
|
||||
public UUID getUUIDByName(String name) {
|
||||
for (Map.Entry<UUID, List<MuteHistoryEntry>> entry : history.entrySet()) {
|
||||
for (MuteHistoryEntry historyEntry : entry.getValue()) {
|
||||
if (historyEntry.playerName != null && historyEntry.playerName.equalsIgnoreCase(name)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,18 +323,56 @@ public class MuteManager {
|
|||
public String reason;
|
||||
public String mutedBy;
|
||||
public long mutedAt;
|
||||
public String playerName;
|
||||
|
||||
public MuteData() {}
|
||||
|
||||
public MuteData(long endTime, String reason, String mutedBy, long mutedAt) {
|
||||
this(endTime, reason, mutedBy, mutedAt, null);
|
||||
}
|
||||
|
||||
public MuteData(long endTime, String reason, String mutedBy, long mutedAt, String playerName) {
|
||||
this.endTime = endTime;
|
||||
this.reason = reason;
|
||||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = mutedAt;
|
||||
this.playerName = playerName;
|
||||
}
|
||||
|
||||
public boolean isPermanent() {
|
||||
return endTime == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись истории мута
|
||||
*/
|
||||
public static class MuteHistoryEntry {
|
||||
public String playerName;
|
||||
public long duration; // 0 = перманентный
|
||||
public String reason;
|
||||
public String mutedBy;
|
||||
public long mutedAt;
|
||||
public boolean unmuted;
|
||||
public String unmutedBy;
|
||||
public long unmutedAt;
|
||||
|
||||
public MuteHistoryEntry() {}
|
||||
|
||||
public MuteHistoryEntry(String playerName, long duration, String reason, String mutedBy,
|
||||
long mutedAt, boolean unmuted, String unmutedBy, long unmutedAt) {
|
||||
this.playerName = playerName;
|
||||
this.duration = duration;
|
||||
this.reason = reason;
|
||||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = mutedAt;
|
||||
this.unmuted = unmuted;
|
||||
this.unmutedBy = unmutedBy;
|
||||
this.unmutedAt = unmutedAt;
|
||||
}
|
||||
|
||||
public boolean isPermanent() {
|
||||
return duration == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue