- Added MuteManager for handling mutes with persistence (mutes.json) - Added /lmute <nick> <time> [-s] [reason] command - Added /lunmute <nick> [-s] command - Time formats: 1d, 2h, 30m, 60s, 0 for permanent - -s flag for silent mute/unmute (no broadcast) - Mute check integrated into ChatListener - Removed LibertyBans API dependency (using built-in system)
201 lines
6.4 KiB
Java
201 lines
6.4 KiB
Java
package com.loki.lochat.managers;
|
||
|
||
import com.google.gson.Gson;
|
||
import com.google.gson.GsonBuilder;
|
||
import com.google.gson.reflect.TypeToken;
|
||
import com.loki.lochat.LoChat;
|
||
|
||
import java.io.*;
|
||
import java.lang.reflect.Type;
|
||
import java.util.Map;
|
||
import java.util.UUID;
|
||
import java.util.concurrent.ConcurrentHashMap;
|
||
|
||
/**
|
||
* Менеджер мутов для LoChat
|
||
*/
|
||
public class MuteManager {
|
||
|
||
private final LoChat plugin;
|
||
private final Map<UUID, MuteData> mutes = new ConcurrentHashMap<>();
|
||
private final File dataFile;
|
||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||
|
||
public MuteManager(LoChat plugin) {
|
||
this.plugin = plugin;
|
||
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
|
||
load();
|
||
}
|
||
|
||
/**
|
||
* Замутить игрока
|
||
* @param uuid UUID игрока
|
||
* @param duration длительность в миллисекундах (0 = перманентный)
|
||
* @param reason причина
|
||
* @param mutedBy кто замутил
|
||
*/
|
||
public void mute(UUID uuid, long duration, String reason, String mutedBy) {
|
||
long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0;
|
||
mutes.put(uuid, new MuteData(endTime, reason, mutedBy, System.currentTimeMillis()));
|
||
saveAsync();
|
||
}
|
||
|
||
/**
|
||
* Размутить игрока
|
||
* @param uuid UUID игрока
|
||
* @return true если игрок был замучен
|
||
*/
|
||
public boolean unmute(UUID uuid) {
|
||
MuteData removed = mutes.remove(uuid);
|
||
if (removed != null) {
|
||
saveAsync();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Проверить, замучен ли игрок
|
||
*/
|
||
public boolean isMuted(UUID uuid) {
|
||
MuteData data = mutes.get(uuid);
|
||
if (data == null) return false;
|
||
|
||
// Проверяем истёк ли мут
|
||
if (data.endTime > 0 && System.currentTimeMillis() > data.endTime) {
|
||
mutes.remove(uuid);
|
||
saveAsync();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Получить данные мута
|
||
*/
|
||
public MuteData getMuteData(UUID uuid) {
|
||
if (!isMuted(uuid)) return null;
|
||
return mutes.get(uuid);
|
||
}
|
||
|
||
/**
|
||
* Получить оставшееся время мута
|
||
*/
|
||
public long getRemainingTime(UUID uuid) {
|
||
MuteData data = mutes.get(uuid);
|
||
if (data == null) return 0;
|
||
if (data.endTime == 0) return -1; // Перманентный
|
||
return Math.max(0, data.endTime - System.currentTimeMillis());
|
||
}
|
||
|
||
/**
|
||
* Форматировать время
|
||
*/
|
||
public String formatTime(long millis) {
|
||
if (millis <= 0) return "0с";
|
||
|
||
long seconds = millis / 1000;
|
||
long minutes = seconds / 60;
|
||
long hours = minutes / 60;
|
||
long days = hours / 24;
|
||
|
||
if (days > 0) {
|
||
return days + "д " + (hours % 24) + "ч";
|
||
} else if (hours > 0) {
|
||
return hours + "ч " + (minutes % 60) + "м";
|
||
} else if (minutes > 0) {
|
||
return minutes + "м " + (seconds % 60) + "с";
|
||
} else {
|
||
return seconds + "с";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Парсинг времени из строки (1d, 2h, 30m, 60s)
|
||
*/
|
||
public long parseTime(String timeStr) {
|
||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||
|
||
timeStr = timeStr.toLowerCase();
|
||
long multiplier = 1000; // базово в миллисекундах
|
||
|
||
try {
|
||
if (timeStr.endsWith("d")) {
|
||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier;
|
||
} else if (timeStr.endsWith("h")) {
|
||
return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * multiplier;
|
||
} else if (timeStr.endsWith("m")) {
|
||
return Long.parseLong(timeStr.replace("m", "")) * 60 * multiplier;
|
||
} else if (timeStr.endsWith("s")) {
|
||
return Long.parseLong(timeStr.replace("s", "")) * multiplier;
|
||
} else {
|
||
// Если без суффикса - считаем минутами
|
||
return Long.parseLong(timeStr) * 60 * multiplier;
|
||
}
|
||
} catch (NumberFormatException e) {
|
||
return -1; // Ошибка парсинга
|
||
}
|
||
}
|
||
|
||
private void load() {
|
||
if (!dataFile.exists()) return;
|
||
|
||
try (Reader reader = new FileReader(dataFile)) {
|
||
Type type = new TypeToken<Map<String, MuteData>>(){}.getType();
|
||
Map<String, MuteData> loaded = gson.fromJson(reader, type);
|
||
if (loaded != null) {
|
||
loaded.forEach((key, value) -> {
|
||
try {
|
||
mutes.put(UUID.fromString(key), value);
|
||
} catch (IllegalArgumentException ignored) {}
|
||
});
|
||
}
|
||
} catch (IOException e) {
|
||
plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
public void save() {
|
||
try {
|
||
if (!dataFile.getParentFile().exists()) {
|
||
dataFile.getParentFile().mkdirs();
|
||
}
|
||
|
||
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
|
||
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||
|
||
try (Writer writer = new FileWriter(dataFile)) {
|
||
gson.toJson(toSave, writer);
|
||
}
|
||
} catch (IOException e) {
|
||
plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
private void saveAsync() {
|
||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, this::save);
|
||
}
|
||
|
||
/**
|
||
* Данные о муте
|
||
*/
|
||
public static class MuteData {
|
||
public long endTime; // 0 = перманентный
|
||
public String reason;
|
||
public String mutedBy;
|
||
public long mutedAt;
|
||
|
||
public MuteData() {}
|
||
|
||
public MuteData(long endTime, String reason, String mutedBy, long mutedAt) {
|
||
this.endTime = endTime;
|
||
this.reason = reason;
|
||
this.mutedBy = mutedBy;
|
||
this.mutedAt = mutedAt;
|
||
}
|
||
|
||
public boolean isPermanent() {
|
||
return endTime == 0;
|
||
}
|
||
}
|
||
}
|