Initial commit: LoChat v1.0.0 with Gradient module
This commit is contained in:
commit
252b0a60db
86 changed files with 8922 additions and 0 deletions
128
src/main/java/com/loki/lochat/managers/AntiSpamManager.java
Normal file
128
src/main/java/com/loki/lochat/managers/AntiSpamManager.java
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class AntiSpamManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<UUID, String> lastMessages = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> lastMessageTime = new ConcurrentHashMap<>();
|
||||
|
||||
public AntiSpamManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public enum SpamResult {
|
||||
ALLOWED,
|
||||
TOO_MANY_CAPS,
|
||||
REPEAT_CHARS,
|
||||
SIMILAR_MESSAGE
|
||||
}
|
||||
|
||||
public SpamResult checkMessage(UUID player, String message) {
|
||||
if (!plugin.getConfigManager().isAntiSpamEnabled()) {
|
||||
return SpamResult.ALLOWED;
|
||||
}
|
||||
|
||||
// Проверка CAPS
|
||||
if (hasTooManyCaps(message)) {
|
||||
return SpamResult.TOO_MANY_CAPS;
|
||||
}
|
||||
|
||||
// Проверка повторяющихся символов
|
||||
if (hasRepeatChars(message)) {
|
||||
return SpamResult.REPEAT_CHARS;
|
||||
}
|
||||
|
||||
// Проверка похожих сообщений
|
||||
if (isSimilarMessage(player, message)) {
|
||||
return SpamResult.SIMILAR_MESSAGE;
|
||||
}
|
||||
|
||||
// Сохраняем сообщение
|
||||
lastMessages.put(player, message.toLowerCase());
|
||||
lastMessageTime.put(player, System.currentTimeMillis());
|
||||
|
||||
return SpamResult.ALLOWED;
|
||||
}
|
||||
|
||||
private boolean hasTooManyCaps(String message) {
|
||||
if (message.length() < 5) return false;
|
||||
|
||||
int maxPercent = plugin.getConfigManager().getMaxCapsPercent();
|
||||
long upperCount = message.chars().filter(Character::isUpperCase).count();
|
||||
long letterCount = message.chars().filter(Character::isLetter).count();
|
||||
|
||||
if (letterCount == 0) return false;
|
||||
|
||||
int percent = (int) ((upperCount * 100) / letterCount);
|
||||
return percent > maxPercent;
|
||||
}
|
||||
|
||||
private boolean hasRepeatChars(String message) {
|
||||
int maxRepeat = plugin.getConfigManager().getMaxRepeatChars();
|
||||
int count = 1;
|
||||
char lastChar = 0;
|
||||
|
||||
for (char c : message.toCharArray()) {
|
||||
if (c == lastChar) {
|
||||
count++;
|
||||
if (count > maxRepeat) return true;
|
||||
} else {
|
||||
count = 1;
|
||||
lastChar = c;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSimilarMessage(UUID player, String message) {
|
||||
String lastMsg = lastMessages.get(player);
|
||||
Long lastTime = lastMessageTime.get(player);
|
||||
|
||||
if (lastMsg == null || lastTime == null) return false;
|
||||
|
||||
int delay = plugin.getConfigManager().getSimilarMessageDelay();
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
|
||||
if (elapsed > delay) return false;
|
||||
|
||||
// Проверка схожести (простая)
|
||||
return message.toLowerCase().equals(lastMsg) ||
|
||||
similarity(message.toLowerCase(), lastMsg) > 0.8;
|
||||
}
|
||||
|
||||
private double similarity(String s1, String s2) {
|
||||
int maxLen = Math.max(s1.length(), s2.length());
|
||||
if (maxLen == 0) return 1.0;
|
||||
|
||||
int distance = levenshteinDistance(s1, s2);
|
||||
return 1.0 - ((double) distance / maxLen);
|
||||
}
|
||||
|
||||
private int levenshteinDistance(String s1, String s2) {
|
||||
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
|
||||
|
||||
for (int i = 0; i <= s1.length(); i++) dp[i][0] = i;
|
||||
for (int j = 0; j <= s2.length(); j++) dp[0][j] = j;
|
||||
|
||||
for (int i = 1; i <= s1.length(); i++) {
|
||||
for (int j = 1; j <= s2.length(); j++) {
|
||||
int cost = s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1;
|
||||
dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost);
|
||||
}
|
||||
}
|
||||
|
||||
return dp[s1.length()][s2.length()];
|
||||
}
|
||||
|
||||
public void clearPlayer(UUID player) {
|
||||
lastMessages.remove(player);
|
||||
lastMessageTime.remove(player);
|
||||
}
|
||||
}
|
||||
174
src/main/java/com/loki/lochat/managers/AutoMessageManager.java
Normal file
174
src/main/java/com/loki/lochat/managers/AutoMessageManager.java
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AutoMessageManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Random random = new Random();
|
||||
private final AtomicInteger currentIndex = new AtomicInteger(0);
|
||||
private ScheduledTask task;
|
||||
|
||||
private Map<String, List<String>> messages;
|
||||
private List<String> messageOrder;
|
||||
private String prefix;
|
||||
private String mode;
|
||||
|
||||
public AutoMessageManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
private void loadMessages() {
|
||||
File file = new File(plugin.getDataFolder(), "automessages.yml");
|
||||
if (!file.exists()) {
|
||||
plugin.saveResource("automessages.yml", false);
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
// Настройки
|
||||
prefix = config.getString("settings.prefix", "&6[&eINFO&6]&r");
|
||||
mode = config.getString("settings.mode", "sequential").toLowerCase();
|
||||
|
||||
// Загружаем сообщения
|
||||
messages = new LinkedHashMap<>();
|
||||
ConfigurationSection msgSection = config.getConfigurationSection("messages");
|
||||
|
||||
if (msgSection != null) {
|
||||
for (String key : msgSection.getKeys(false)) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
if (msgSection.isList(key)) {
|
||||
lines.addAll(msgSection.getStringList(key));
|
||||
} else {
|
||||
String single = msgSection.getString(key);
|
||||
if (single != null) {
|
||||
lines.add(single);
|
||||
}
|
||||
}
|
||||
|
||||
if (!lines.isEmpty()) {
|
||||
messages.put(key, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем порядок
|
||||
messageOrder = new ArrayList<>();
|
||||
|
||||
if (mode.equals("custom")) {
|
||||
String orderStr = config.getString("settings.order", "");
|
||||
for (String item : orderStr.split(",")) {
|
||||
String trimmed = item.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
try {
|
||||
int index = Integer.parseInt(trimmed) - 1;
|
||||
List<String> keys = new ArrayList<>(messages.keySet());
|
||||
if (index >= 0 && index < keys.size()) {
|
||||
messageOrder.add(keys.get(index));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
if (messages.containsKey(trimmed)) {
|
||||
messageOrder.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messageOrder.isEmpty()) {
|
||||
messageOrder.addAll(messages.keySet());
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Загружено " + messages.size() + " автосообщений");
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (!plugin.getConfigManager().isAutoMessagesEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (messages == null || messages.isEmpty()) {
|
||||
plugin.getLogger().warning("Автосообщения не настроены!");
|
||||
return;
|
||||
}
|
||||
|
||||
int intervalSeconds = plugin.getConfigManager().getAutoMessagesInterval();
|
||||
long intervalTicks = intervalSeconds * 20L;
|
||||
|
||||
task = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(
|
||||
plugin,
|
||||
scheduledTask -> broadcastNextMessage(),
|
||||
intervalTicks,
|
||||
intervalTicks
|
||||
);
|
||||
|
||||
plugin.getLogger().info("Автосообщения запущены (интервал: " + intervalSeconds + " сек, режим: " + mode + ")");
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (task != null && !task.isCancelled()) {
|
||||
task.cancel();
|
||||
task = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastNextMessage() {
|
||||
if (messageOrder.isEmpty() || Bukkit.getOnlinePlayers().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String messageKey;
|
||||
|
||||
if (mode.equals("random")) {
|
||||
messageKey = messageOrder.get(random.nextInt(messageOrder.size()));
|
||||
} else {
|
||||
int index = currentIndex.getAndUpdate(i -> (i + 1) % messageOrder.size());
|
||||
messageKey = messageOrder.get(index);
|
||||
}
|
||||
|
||||
List<String> lines = messages.get(messageKey);
|
||||
if (lines == null || lines.isEmpty()) return;
|
||||
|
||||
boolean isFirstLine = true;
|
||||
for (String line : lines) {
|
||||
// Заменяем смайлики через EmojiManager
|
||||
String processed = ChatFormatter.replaceEmojis(line);
|
||||
|
||||
// Заменяем плейсхолдеры
|
||||
processed = processed.replace("%online%", String.valueOf(Bukkit.getOnlinePlayers().size()));
|
||||
|
||||
// Добавляем префикс к первой непустой строке
|
||||
Component formatted;
|
||||
if (isFirstLine && !line.trim().isEmpty()) {
|
||||
formatted = ChatFormatter.parse(prefix + " " + processed);
|
||||
isFirstLine = false;
|
||||
} else {
|
||||
formatted = ChatFormatter.parse(processed);
|
||||
}
|
||||
|
||||
for (var player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(formatted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
stop();
|
||||
loadMessages();
|
||||
currentIndex.set(0);
|
||||
start();
|
||||
}
|
||||
}
|
||||
115
src/main/java/com/loki/lochat/managers/ChatManager.java
Normal file
115
src/main/java/com/loki/lochat/managers/ChatManager.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.DistanceUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ChatManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public ChatManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void sendGlobalMessage(Player sender, String message) {
|
||||
if (!plugin.getConfigManager().isGlobalEnabled()) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("global.disabled")));
|
||||
return;
|
||||
}
|
||||
|
||||
// Обработка упоминаний @ник
|
||||
Set<Player> mentionedPlayers = new HashSet<>();
|
||||
String processedMessage = plugin.getMentionManager().processMentions(message, mentionedPlayers);
|
||||
|
||||
String format = plugin.getConfigManager().getGlobalFormat();
|
||||
String prefix = plugin.getConfigManager().getGlobalPrefix();
|
||||
boolean hasColor = sender.hasPermission("chat.global.color");
|
||||
|
||||
// Отправка каждому игроку персонализированное сообщение
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (isGlobalChatDisabled(player.getUniqueId())) continue;
|
||||
|
||||
// Персонализируем сообщение для этого игрока (выделяем его ник)
|
||||
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
|
||||
|
||||
Component formatted = ChatFormatter.formatGlobalMessage(format, prefix, sender, personalizedMsg, hasColor);
|
||||
player.sendMessage(formatted);
|
||||
|
||||
// Проверяем упоминание ника без @ и уведомляем
|
||||
plugin.getMentionManager().notifyIfMentioned(message, player, sender);
|
||||
}
|
||||
|
||||
// Уведомление упомянутых через @
|
||||
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
||||
|
||||
plugin.getLogger().info("[G] " + sender.getName() + ": " + ChatFormatter.stripTags(message));
|
||||
}
|
||||
|
||||
public void sendLocalMessage(Player sender, String message) {
|
||||
if (!plugin.getConfigManager().isLocalEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Обработка упоминаний @ник
|
||||
Set<Player> mentionedPlayers = new HashSet<>();
|
||||
String processedMessage = plugin.getMentionManager().processMentions(message, mentionedPlayers);
|
||||
|
||||
int radius = plugin.getConfigManager().getLocalRadius();
|
||||
String format = plugin.getConfigManager().getLocalFormat();
|
||||
String prefix = plugin.getConfigManager().getLocalPrefix();
|
||||
boolean hasColor = sender.hasPermission("chat.local.color");
|
||||
|
||||
boolean foundRecipient = false;
|
||||
Set<Player> recipients = new HashSet<>();
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
// Персонализируем сообщение для этого игрока
|
||||
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
|
||||
|
||||
Component formatted = ChatFormatter.formatLocalMessage(format, prefix, sender, personalizedMsg, hasColor);
|
||||
player.sendMessage(formatted);
|
||||
recipients.add(player);
|
||||
|
||||
// Проверяем упоминание ника без @
|
||||
plugin.getMentionManager().notifyIfMentioned(message, player, sender);
|
||||
|
||||
if (!player.equals(sender)) {
|
||||
foundRecipient = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Уведомление упомянутых через @ (только тех кто в радиусе)
|
||||
mentionedPlayers.retainAll(recipients);
|
||||
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
||||
|
||||
if (!foundRecipient) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("local.no-players")));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean toggleGlobalChat(UUID player) {
|
||||
if (globalChatDisabled.contains(player)) {
|
||||
globalChatDisabled.remove(player);
|
||||
return true;
|
||||
} else {
|
||||
globalChatDisabled.add(player);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isGlobalChatDisabled(UUID player) {
|
||||
return globalChatDisabled.contains(player);
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/loki/lochat/managers/CooldownManager.java
Normal file
45
src/main/java/com/loki/lochat/managers/CooldownManager.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CooldownManager {
|
||||
|
||||
private final Map<UUID, Long> globalCooldowns = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> localCooldowns = new ConcurrentHashMap<>();
|
||||
|
||||
public boolean isOnCooldown(UUID player, String chatType, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns;
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
if (lastTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return elapsed < cooldownSeconds;
|
||||
}
|
||||
|
||||
public int getRemainingCooldown(UUID player, String chatType, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns;
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
if (lastTime == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return Math.max(0, cooldownSeconds - (int) elapsed);
|
||||
}
|
||||
|
||||
public void setCooldown(UUID player, String chatType) {
|
||||
Map<UUID, Long> cooldowns = chatType.equals("global") ? globalCooldowns : localCooldowns;
|
||||
cooldowns.put(player, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public void removeCooldown(UUID player) {
|
||||
globalCooldowns.remove(player);
|
||||
localCooldowns.remove(player);
|
||||
}
|
||||
}
|
||||
100
src/main/java/com/loki/lochat/managers/EmojiManager.java
Normal file
100
src/main/java/com/loki/lochat/managers/EmojiManager.java
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EmojiManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, String> emojis = new HashMap<>();
|
||||
private boolean enabled;
|
||||
private boolean requirePermission;
|
||||
|
||||
public EmojiManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
File file = new File(plugin.getDataFolder(), "emojis.yml");
|
||||
if (!file.exists()) {
|
||||
plugin.saveResource("emojis.yml", false);
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
emojis.clear();
|
||||
|
||||
// Настройки
|
||||
enabled = config.getBoolean("settings.enabled", true);
|
||||
requirePermission = config.getBoolean("settings.require-permission", false);
|
||||
|
||||
// Загружаем все категории
|
||||
for (String category : config.getKeys(false)) {
|
||||
if (category.equals("settings")) continue;
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection(category);
|
||||
if (section != null) {
|
||||
for (String key : section.getKeys(false)) {
|
||||
String value = section.getString(key);
|
||||
if (value != null) {
|
||||
emojis.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Загружено " + emojis.size() + " смайликов");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет смайлики в сообщении
|
||||
*/
|
||||
public String process(String message, Player player) {
|
||||
if (!enabled) return message;
|
||||
|
||||
// Проверка права
|
||||
if (requirePermission && player != null && !player.hasPermission("chat.emoji.use")) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет смайлики без проверки прав (для автосообщений)
|
||||
*/
|
||||
public String process(String message) {
|
||||
if (!enabled) return message;
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, String> getEmojis() {
|
||||
return emojis;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
61
src/main/java/com/loki/lochat/managers/FilterManager.java
Normal file
61
src/main/java/com/loki/lochat/managers/FilterManager.java
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FilterManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public FilterManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public enum FilterResult {
|
||||
ALLOWED,
|
||||
CENSORED,
|
||||
BLOCKED,
|
||||
WARNED
|
||||
}
|
||||
|
||||
public FilterResult checkMessage(String message) {
|
||||
if (!plugin.getConfigManager().isFilterEnabled()) {
|
||||
return FilterResult.ALLOWED;
|
||||
}
|
||||
|
||||
List<String> badWords = plugin.getConfigManager().getFilterWords();
|
||||
String lowerMessage = message.toLowerCase();
|
||||
|
||||
for (String word : badWords) {
|
||||
if (lowerMessage.contains(word.toLowerCase())) {
|
||||
String action = plugin.getConfigManager().getFilterAction();
|
||||
return switch (action) {
|
||||
case "block" -> FilterResult.BLOCKED;
|
||||
case "warn" -> FilterResult.WARNED;
|
||||
default -> FilterResult.CENSORED;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return FilterResult.ALLOWED;
|
||||
}
|
||||
|
||||
public String censorMessage(String message) {
|
||||
if (!plugin.getConfigManager().isFilterEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
List<String> badWords = plugin.getConfigManager().getFilterWords();
|
||||
String replacement = plugin.getConfigManager().getFilterReplacement();
|
||||
String result = message;
|
||||
|
||||
for (String word : badWords) {
|
||||
Pattern pattern = Pattern.compile("(?i)" + Pattern.quote(word));
|
||||
result = pattern.matcher(result).replaceAll(replacement);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
97
src/main/java/com/loki/lochat/managers/IgnoreManager.java
Normal file
97
src/main/java/com/loki/lochat/managers/IgnoreManager.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class IgnoreManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final File ignoreFile;
|
||||
private final Map<UUID, Set<UUID>> ignoreMap = new ConcurrentHashMap<>();
|
||||
|
||||
public IgnoreManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreFile = new File(plugin.getDataFolder(), "ignores.yml");
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
if (!ignoreFile.exists()) return;
|
||||
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(ignoreFile);
|
||||
for (String key : config.getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(key);
|
||||
List<String> ignoredList = config.getStringList(key);
|
||||
Set<UUID> ignored = ConcurrentHashMap.newKeySet();
|
||||
|
||||
for (String ignoredStr : ignoredList) {
|
||||
try {
|
||||
ignored.add(UUID.fromString(ignoredStr));
|
||||
} catch (Exception ignored2) {}
|
||||
}
|
||||
|
||||
if (!ignored.isEmpty()) {
|
||||
ignoreMap.put(uuid, ignored);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
|
||||
for (Map.Entry<UUID, Set<UUID>> entry : ignoreMap.entrySet()) {
|
||||
List<String> ignoredList = new ArrayList<>();
|
||||
for (UUID ignored : entry.getValue()) {
|
||||
ignoredList.add(ignored.toString());
|
||||
}
|
||||
config.set(entry.getKey().toString(), ignoredList);
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(ignoreFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Не удалось сохранить игноры: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isIgnoring(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
return ignored != null && ignored.contains(target);
|
||||
}
|
||||
|
||||
public boolean addIgnore(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.computeIfAbsent(player, k -> ConcurrentHashMap.newKeySet());
|
||||
boolean added = ignored.add(target);
|
||||
if (added) save();
|
||||
return added;
|
||||
}
|
||||
|
||||
public boolean removeIgnore(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
if (ignored == null) return false;
|
||||
boolean removed = ignored.remove(target);
|
||||
if (removed) save();
|
||||
return removed;
|
||||
}
|
||||
|
||||
public Set<UUID> getIgnoredPlayers(UUID player) {
|
||||
return ignoreMap.getOrDefault(player, Collections.emptySet());
|
||||
}
|
||||
|
||||
public int getIgnoredCount(UUID player) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
return ignored == null ? 0 : ignored.size();
|
||||
}
|
||||
|
||||
public void clearIgnores(UUID player) {
|
||||
ignoreMap.remove(player);
|
||||
save();
|
||||
}
|
||||
}
|
||||
141
src/main/java/com/loki/lochat/managers/MentionManager.java
Normal file
141
src/main/java/com/loki/lochat/managers/MentionManager.java
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MentionManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private static final Pattern MENTION_PATTERN = Pattern.compile("@(\\w+)");
|
||||
|
||||
// Хранит для какого игрока какие части сообщения выделить
|
||||
private final Map<Player, Set<String>> highlightedNicks = new HashMap<>();
|
||||
|
||||
public MentionManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает упоминания в сообщении
|
||||
* Возвращает сообщение с подсвеченными никами
|
||||
*/
|
||||
public String processMentions(String message, Set<Player> mentionedPlayers) {
|
||||
if (!plugin.getConfigManager().isMentionsEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
highlightedNicks.clear();
|
||||
String highlight = plugin.getConfigManager().getMentionHighlight();
|
||||
Matcher matcher = MENTION_PATTERN.matcher(message);
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
while (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
Player mentioned = Bukkit.getPlayer(name);
|
||||
|
||||
if (mentioned != null && mentioned.isOnline()) {
|
||||
mentionedPlayers.add(mentioned);
|
||||
String replacement = highlight.replace("{player}", mentioned.getName());
|
||||
matcher.appendReplacement(result, replacement);
|
||||
|
||||
// Запоминаем что этот игрок упомянут
|
||||
highlightedNicks.computeIfAbsent(mentioned, k -> new HashSet<>()).add(mentioned.getName());
|
||||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт персонализированное сообщение для игрока
|
||||
* Если его ник упомянут — выделяет его особым образом
|
||||
*/
|
||||
public String getPersonalizedMessage(String message, Player viewer) {
|
||||
if (!plugin.getConfigManager().isMentionsEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String viewerName = viewer.getName();
|
||||
String result = message;
|
||||
|
||||
// Проверяем упоминание через @ник
|
||||
String highlight = plugin.getConfigManager().getMentionHighlight();
|
||||
String highlightedName = highlight.replace("{player}", viewerName);
|
||||
|
||||
// Если в сообщении есть выделенный ник этого игрока — добавляем дополнительное выделение
|
||||
if (result.contains(highlightedName)) {
|
||||
// Уже выделено через processMentions, добавляем эффект
|
||||
String extraHighlight = "<bold>" + highlightedName + "</bold>";
|
||||
result = result.replace(highlightedName, extraHighlight);
|
||||
}
|
||||
|
||||
// Также проверяем просто ник в сообщении (без @)
|
||||
// Регистронезависимый поиск
|
||||
Pattern nickPattern = Pattern.compile("(?i)\\b" + Pattern.quote(viewerName) + "\\b");
|
||||
Matcher nickMatcher = nickPattern.matcher(result);
|
||||
|
||||
if (nickMatcher.find()) {
|
||||
// Ник найден в сообщении — выделяем для этого игрока
|
||||
String selfHighlight = plugin.getConfigManager().getSelfMentionHighlight();
|
||||
result = nickMatcher.replaceAll(selfHighlight.replace("{player}", viewerName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, упомянут ли игрок в сообщении (по нику без @)
|
||||
*/
|
||||
public boolean isPlayerMentioned(String message, Player player) {
|
||||
String name = player.getName();
|
||||
Pattern pattern = Pattern.compile("(?i)\\b" + Pattern.quote(name) + "\\b");
|
||||
return pattern.matcher(message).find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Уведомляет упомянутых игроков звуком
|
||||
*/
|
||||
public void notifyMentioned(Set<Player> players) {
|
||||
if (!plugin.getConfigManager().isMentionSoundEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String soundName = plugin.getConfigManager().getMentionSoundType();
|
||||
Sound sound;
|
||||
try {
|
||||
sound = Sound.valueOf(soundName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sound = Sound.BLOCK_NOTE_BLOCK_PLING;
|
||||
}
|
||||
|
||||
for (Player player : players) {
|
||||
player.playSound(player.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Уведомляет игрока если его ник упомянут (без @)
|
||||
*/
|
||||
public void notifyIfMentioned(String message, Player viewer, Player sender) {
|
||||
if (viewer.equals(sender)) return;
|
||||
if (!plugin.getConfigManager().isMentionSoundEnabled()) return;
|
||||
|
||||
if (isPlayerMentioned(message, viewer)) {
|
||||
String soundName = plugin.getConfigManager().getMentionSoundType();
|
||||
try {
|
||||
Sound sound = Sound.valueOf(soundName);
|
||||
viewer.playSound(viewer.getLocation(), sound, 1.0f, 1.0f);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/main/java/com/loki/lochat/managers/MuteManager.java
Normal file
136
src/main/java/com/loki/lochat/managers/MuteManager.java
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MuteManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final File muteFile;
|
||||
private final Map<UUID, Long> mutedPlayers = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, String> muteReasons = new ConcurrentHashMap<>();
|
||||
|
||||
public MuteManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteFile = new File(plugin.getDataFolder(), "mutes.yml");
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
if (!muteFile.exists()) return;
|
||||
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(muteFile);
|
||||
for (String key : config.getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(key);
|
||||
long muteEnd = config.getLong(key + ".end");
|
||||
String reason = config.getString(key + ".reason");
|
||||
|
||||
// Пропускаем истёкшие муты
|
||||
if (muteEnd != 0 && System.currentTimeMillis() > muteEnd) continue;
|
||||
|
||||
mutedPlayers.put(uuid, muteEnd);
|
||||
if (reason != null) muteReasons.put(uuid, reason);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
|
||||
for (Map.Entry<UUID, Long> entry : mutedPlayers.entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
config.set(key + ".end", entry.getValue());
|
||||
String reason = muteReasons.get(entry.getKey());
|
||||
if (reason != null) config.set(key + ".reason", reason);
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(muteFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Не удалось сохранить муты: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMuted(UUID player) {
|
||||
Long muteEnd = mutedPlayers.get(player);
|
||||
if (muteEnd == null) return false;
|
||||
|
||||
if (muteEnd == 0) return true;
|
||||
|
||||
if (System.currentTimeMillis() > muteEnd) {
|
||||
mutedPlayers.remove(player);
|
||||
muteReasons.remove(player);
|
||||
save();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void mute(UUID player, long durationSeconds, String reason) {
|
||||
long muteEnd = durationSeconds <= 0 ? 0 : System.currentTimeMillis() + (durationSeconds * 1000);
|
||||
mutedPlayers.put(player, muteEnd);
|
||||
if (reason != null && !reason.isEmpty()) {
|
||||
muteReasons.put(player, reason);
|
||||
}
|
||||
save();
|
||||
}
|
||||
|
||||
public void unmute(UUID player) {
|
||||
mutedPlayers.remove(player);
|
||||
muteReasons.remove(player);
|
||||
save();
|
||||
}
|
||||
|
||||
public String getRemainingTime(UUID player) {
|
||||
Long muteEnd = mutedPlayers.get(player);
|
||||
if (muteEnd == null) return null;
|
||||
if (muteEnd == 0) return "навсегда";
|
||||
|
||||
long remaining = (muteEnd - System.currentTimeMillis()) / 1000;
|
||||
if (remaining <= 0) return null;
|
||||
|
||||
return formatTime(remaining);
|
||||
}
|
||||
|
||||
public String getReason(UUID player) {
|
||||
return muteReasons.get(player);
|
||||
}
|
||||
|
||||
public boolean isAlreadyMuted(UUID player) {
|
||||
return mutedPlayers.containsKey(player);
|
||||
}
|
||||
|
||||
private String formatTime(long seconds) {
|
||||
if (seconds < 60) return seconds + " сек";
|
||||
if (seconds < 3600) return (seconds / 60) + " мин";
|
||||
if (seconds < 86400) return (seconds / 3600) + " ч";
|
||||
return (seconds / 86400) + " д";
|
||||
}
|
||||
|
||||
public static long parseTime(String time) {
|
||||
if (time == null || time.isEmpty()) return -1;
|
||||
|
||||
try {
|
||||
char unit = time.charAt(time.length() - 1);
|
||||
long value = Long.parseLong(time.substring(0, time.length() - 1));
|
||||
|
||||
return switch (Character.toLowerCase(unit)) {
|
||||
case 's' -> value;
|
||||
case 'm' -> value * 60;
|
||||
case 'h' -> value * 3600;
|
||||
case 'd' -> value * 86400;
|
||||
default -> Long.parseLong(time);
|
||||
};
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/main/java/com/loki/lochat/managers/PMManager.java
Normal file
27
src/main/java/com/loki/lochat/managers/PMManager.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PMManager {
|
||||
|
||||
// UUID игрока -> UUID последнего собеседника
|
||||
private final Map<UUID, UUID> lastConversation = new ConcurrentHashMap<>();
|
||||
|
||||
public void setLastConversation(UUID player, UUID target) {
|
||||
lastConversation.put(player, target);
|
||||
}
|
||||
|
||||
public UUID getLastConversation(UUID player) {
|
||||
return lastConversation.get(player);
|
||||
}
|
||||
|
||||
public void removeConversation(UUID player) {
|
||||
lastConversation.remove(player);
|
||||
}
|
||||
|
||||
public boolean hasConversation(UUID player) {
|
||||
return lastConversation.containsKey(player);
|
||||
}
|
||||
}
|
||||
52
src/main/java/com/loki/lochat/managers/SpyManager.java
Normal file
52
src/main/java/com/loki/lochat/managers/SpyManager.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SpyManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Set<UUID> spyEnabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public SpyManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean toggleSpy(UUID player) {
|
||||
if (spyEnabled.contains(player)) {
|
||||
spyEnabled.remove(player);
|
||||
return false;
|
||||
} else {
|
||||
spyEnabled.add(player);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSpying(UUID player) {
|
||||
return spyEnabled.contains(player);
|
||||
}
|
||||
|
||||
public void broadcastPM(Player sender, Player receiver, String message) {
|
||||
String format = plugin.getMessageConfig().get("spy.format");
|
||||
|
||||
for (UUID spyUuid : spyEnabled) {
|
||||
Player spy = Bukkit.getPlayer(spyUuid);
|
||||
if (spy != null && spy.isOnline() && !spy.equals(sender) && !spy.equals(receiver)) {
|
||||
spy.sendMessage(ChatFormatter.parse(format
|
||||
.replace("{sender}", sender.getName())
|
||||
.replace("{receiver}", receiver.getName())
|
||||
.replace("{message}", message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeSpy(UUID player) {
|
||||
spyEnabled.remove(player);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue