fix all
This commit is contained in:
parent
5088e3ad1c
commit
d1d2b93742
82 changed files with 4008 additions and 3032 deletions
|
|
@ -11,6 +11,7 @@ import com.loki.lochat.gradient.GradientModule;
|
|||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.integrations.LibertyBansHook;
|
||||
import com.loki.lochat.integrations.PlaceholderAPIHook;
|
||||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.listener.ChatEventListener;
|
||||
import com.loki.lochat.listener.DiscordEventListener;
|
||||
import com.loki.lochat.listener.PlayerEventListener;
|
||||
|
|
@ -33,6 +34,8 @@ public final class LoChat extends JavaPlugin {
|
|||
private AutoMessageManager autoMessageManager;
|
||||
private CustomCommandManager customCommandManager;
|
||||
private DiscordIntegration discordIntegration;
|
||||
private CommandManager commandManager;
|
||||
private AdvancedMessageFilter advancedMessageFilter;
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() {
|
||||
|
|
@ -60,7 +63,8 @@ public final class LoChat extends JavaPlugin {
|
|||
initIntegrations();
|
||||
|
||||
// Команды и слушатели
|
||||
registerCommands();
|
||||
commandManager = new CommandManager(this);
|
||||
commandManager.registerCommands();
|
||||
registerListeners();
|
||||
registerPluginChannels();
|
||||
|
||||
|
|
@ -74,6 +78,10 @@ public final class LoChat extends JavaPlugin {
|
|||
public void onDisable() {
|
||||
if (autoMessageManager != null) autoMessageManager.stop();
|
||||
|
||||
// Сохраняем статистику
|
||||
com.loki.lochat.api.service.PlayerDataService pds = serviceRegistry.get(com.loki.lochat.api.service.PlayerDataService.class);
|
||||
if (pds != null) pds.saveAll();
|
||||
|
||||
// Сохранение данных через сервисы
|
||||
IgnoreService ignoreService = serviceRegistry.get(IgnoreService.class);
|
||||
if (ignoreService != null) ignoreService.save();
|
||||
|
|
@ -84,6 +92,9 @@ public final class LoChat extends JavaPlugin {
|
|||
NickService nickService = serviceRegistry.get(NickService.class);
|
||||
if (nickService != null) nickService.save();
|
||||
|
||||
// Останавливаем Discord executor
|
||||
if (discordIntegration != null) discordIntegration.shutdown();
|
||||
|
||||
if (gradientModule != null) gradientModule.shutdown();
|
||||
getLogger().info("LoChat отключен!");
|
||||
}
|
||||
|
|
@ -108,62 +119,18 @@ public final class LoChat extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
// Старый метод регистрации команд - заменен на CommandManager
|
||||
/*
|
||||
private void registerCommands() {
|
||||
getCommand("g").setExecutor(new GlobalChatCommand(this));
|
||||
getCommand("l").setExecutor(new LocalChatCommand(this));
|
||||
getCommand("msg").setExecutor(new MsgCommand(this));
|
||||
getCommand("reply").setExecutor(new ReplyCommand(this));
|
||||
getCommand("ignore").setExecutor(new IgnoreCommand(this));
|
||||
getCommand("unignore").setExecutor(new UnignoreCommand(this));
|
||||
getCommand("ignorelist").setExecutor(new IgnoreListCommand(this));
|
||||
getCommand("announce").setExecutor(new AnnounceCommand(this));
|
||||
getCommand("chatspy").setExecutor(new ChatSpyCommand(this));
|
||||
getCommand("clearchat").setExecutor(new ClearChatCommand(this));
|
||||
|
||||
NickCommand nickCmd = new NickCommand(this);
|
||||
getCommand("nick").setExecutor(nickCmd);
|
||||
getCommand("nick").setTabCompleter(nickCmd);
|
||||
|
||||
LoChatCommand loChatCmd = new LoChatCommand(this);
|
||||
getCommand("lochat").setExecutor(loChatCmd);
|
||||
getCommand("lochat").setTabCompleter(loChatCmd);
|
||||
|
||||
MuteCommand muteCmd = new MuteCommand(this);
|
||||
getCommand("lmute").setExecutor(muteCmd);
|
||||
getCommand("lmute").setTabCompleter(muteCmd);
|
||||
|
||||
UnmuteCommand unmuteCmd = new UnmuteCommand(this);
|
||||
getCommand("lunmute").setExecutor(unmuteCmd);
|
||||
getCommand("lunmute").setTabCompleter(unmuteCmd);
|
||||
|
||||
getCommand("lmutelist").setExecutor(new MuteListCommand(this));
|
||||
|
||||
MuteHistoryCommand historyCmd = new MuteHistoryCommand(this);
|
||||
getCommand("lmutehistory").setExecutor(historyCmd);
|
||||
getCommand("lmutehistory").setTabCompleter(historyCmd);
|
||||
|
||||
MuteBlameCommand blameCmd = new MuteBlameCommand(this);
|
||||
getCommand("lmuteblame").setExecutor(blameCmd);
|
||||
getCommand("lmuteblame").setTabCompleter(blameCmd);
|
||||
|
||||
ClearChatConfigCommand clearCfgCmd = new ClearChatConfigCommand(this);
|
||||
getCommand("clearchatconfig").setExecutor(clearCfgCmd);
|
||||
getCommand("clearchatconfig").setTabCompleter(clearCfgCmd);
|
||||
|
||||
// Custom commands управление
|
||||
CustomCommandsCommand customCommandsCmd = new CustomCommandsCommand(this, customCommandManager);
|
||||
getCommand("customcommands").setExecutor(customCommandsCmd);
|
||||
getCommand("customcommands").setTabCompleter(customCommandsCmd);
|
||||
|
||||
// Discord admin команда (отдельно от кастомной /discord)
|
||||
DiscordCommand discordAdminCmd = new DiscordCommand(this, discordIntegration);
|
||||
getCommand("discordadmin").setExecutor(discordAdminCmd);
|
||||
getCommand("discordadmin").setTabCompleter(discordAdminCmd);
|
||||
// Код перенесен в CommandManager для лучшей организации
|
||||
}
|
||||
*/
|
||||
|
||||
private void registerListeners() {
|
||||
getServer().getPluginManager().registerEvents(new ChatEventListener(this, serviceRegistry), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerEventListener(serviceRegistry), this);
|
||||
// Создаём один экземпляр фильтра и передаём в оба листенера — flood/spam трекеры общие
|
||||
advancedMessageFilter = new AdvancedMessageFilter(getConfig(), this);
|
||||
getServer().getPluginManager().registerEvents(new ChatEventListener(this, serviceRegistry, advancedMessageFilter), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerEventListener(serviceRegistry, advancedMessageFilter), this);
|
||||
|
||||
// Discord listener
|
||||
if (discordIntegration.isEnabled()) {
|
||||
|
|
@ -208,6 +175,10 @@ public final class LoChat extends JavaPlugin {
|
|||
return customCommandManager;
|
||||
}
|
||||
|
||||
public CommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
}
|
||||
|
||||
public DiscordIntegration getDiscordIntegration() {
|
||||
return discordIntegration;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Команда для настройки сообщения очистки чата
|
||||
* /clearchatconfig <enable|disable|message> [текст]
|
||||
*/
|
||||
public class ClearChatConfigCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public ClearChatConfigCommand(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.clearchat.config")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatFormatter.parse("&6Использование:"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/clearchatconfig enable &7- включить сообщение"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/clearchatconfig disable &7- отключить сообщение"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/clearchatconfig message <текст> &7- установить сообщение"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/clearchatconfig status &7- показать статус"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = args[0].toLowerCase();
|
||||
|
||||
switch (action) {
|
||||
case "enable" -> {
|
||||
plugin.getConfigManager().setClearChatMessageEnabled(true);
|
||||
sender.sendMessage(ChatFormatter.parse("&aСообщение очистки чата включено"));
|
||||
}
|
||||
case "disable" -> {
|
||||
plugin.getConfigManager().setClearChatMessageEnabled(false);
|
||||
sender.sendMessage(ChatFormatter.parse("&cСообщение очистки чата отключено"));
|
||||
}
|
||||
case "message" -> {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatFormatter.parse("&cУкажите текст сообщения!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
plugin.getConfigManager().setClearChatMessage(message);
|
||||
sender.sendMessage(ChatFormatter.parse("&aСообщение установлено: &f" + message));
|
||||
}
|
||||
case "status" -> {
|
||||
boolean enabled = plugin.getConfigManager().isClearChatMessageEnabled();
|
||||
String message = plugin.getConfigManager().getClearChatMessage();
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("&6Статус сообщения очистки чата:"));
|
||||
sender.sendMessage(ChatFormatter.parse("&eВключено: " + (enabled ? "&aДа" : "&cНет")));
|
||||
sender.sendMessage(ChatFormatter.parse("&eСообщение: &f" + message));
|
||||
}
|
||||
default -> {
|
||||
sender.sendMessage(ChatFormatter.parse("&cНеизвестная команда! Используйте: enable, disable, message, status"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("enable", "disable", "message", "status").stream()
|
||||
.filter(s -> s.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
106
src/main/java/com/loki/lochat/commands/CommandManager.java
Normal file
106
src/main/java/com/loki/lochat/commands/CommandManager.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.commands.admin.*;
|
||||
import com.loki.lochat.commands.chat.*;
|
||||
import com.loki.lochat.commands.messaging.*;
|
||||
import com.loki.lochat.commands.moderation.*;
|
||||
import com.loki.lochat.commands.nick.*;
|
||||
import com.loki.lochat.commands.rp.*;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Централизованная регистрация всех команд плагина.
|
||||
*/
|
||||
public class CommandManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, com.loki.lochat.commands.base.BaseCommand> commands = new HashMap<>();
|
||||
|
||||
public CommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void registerCommands() {
|
||||
|
||||
// ── Чат ──────────────────────────────────────────────────────────────
|
||||
reg("g", new GlobalChatCommand(plugin));
|
||||
reg("l", new LocalChatCommand(plugin));
|
||||
|
||||
// ── Личные сообщения / игнор ──────────────────────────────────────────
|
||||
reg("msg", new MsgCommand(plugin));
|
||||
reg("reply", new ReplyCommand(plugin));
|
||||
reg("ignore", new IgnoreCommand(plugin));
|
||||
reg("unignore", new UnignoreCommand(plugin));
|
||||
reg("ignorelist", new IgnoreListCommand(plugin));
|
||||
|
||||
// ── Модерация ─────────────────────────────────────────────────────────
|
||||
regTab("lmute", new MuteCommand(plugin));
|
||||
regTab("lunmute", new UnmuteCommand(plugin));
|
||||
reg("lmutelist", new MuteListCommand(plugin));
|
||||
regTab("lmutehistory", new MuteHistoryCommand(plugin));
|
||||
regTab("lmuteblame", new MuteBlameCommand(plugin));
|
||||
|
||||
// ── Ник / профиль ─────────────────────────────────────────────────────
|
||||
regTab("nick", new NickCommand(plugin));
|
||||
reg("playerinfo", new PlayerInfoCommand(plugin));
|
||||
|
||||
// ── Админ ─────────────────────────────────────────────────────────────
|
||||
reg("announce", new AnnounceCommand(plugin));
|
||||
reg("chatspy", new ChatSpyCommand(plugin));
|
||||
reg("clearchat", new ClearChatCommand(plugin));
|
||||
regTab("clearchatconfig", new ClearChatConfigCommand(plugin));
|
||||
regTab("lochat", new LoChatCommand(plugin));
|
||||
reg("lochatreload", new ReloadConfigCommand(plugin));
|
||||
regTab("discordadmin", new DiscordCommand(plugin, plugin.getDiscordIntegration()));
|
||||
|
||||
// ── RP ────────────────────────────────────────────────────────────────
|
||||
registerBaseCommand("me", new MeCommand(plugin));
|
||||
registerBaseCommand("try", new TryCommand(plugin));
|
||||
registerBaseCommand("do", new DoCommand(plugin));
|
||||
regTab("roll", new RollCommand(plugin));
|
||||
|
||||
// ── Кастомные команды ─────────────────────────────────────────────────
|
||||
regTab("customcommands", new CustomCommandsCommand(plugin, plugin.getCustomCommandManager()));
|
||||
|
||||
plugin.getLogger().info("[LoChat] Команды зарегистрированы.");
|
||||
}
|
||||
|
||||
// ── Вспомогательные методы ────────────────────────────────────────────────
|
||||
|
||||
/** Регистрирует команду только как CommandExecutor */
|
||||
private void reg(String name, org.bukkit.command.CommandExecutor executor) {
|
||||
PluginCommand cmd = plugin.getCommand(name);
|
||||
if (cmd != null) {
|
||||
cmd.setExecutor(executor);
|
||||
} else {
|
||||
plugin.getLogger().warning("[LoChat] Команда '" + name + "' не найдена в plugin.yml!");
|
||||
}
|
||||
}
|
||||
|
||||
/** Регистрирует команду как CommandExecutor + TabCompleter */
|
||||
private void regTab(String name, Object executor) {
|
||||
PluginCommand cmd = plugin.getCommand(name);
|
||||
if (cmd != null) {
|
||||
cmd.setExecutor((org.bukkit.command.CommandExecutor) executor);
|
||||
if (executor instanceof org.bukkit.command.TabCompleter tc) cmd.setTabCompleter(tc);
|
||||
} else {
|
||||
plugin.getLogger().warning("[LoChat] Команда '" + name + "' не найдена в plugin.yml!");
|
||||
}
|
||||
}
|
||||
|
||||
/** Регистрирует BaseCommand (executor + tab-completer из одного класса) */
|
||||
private void registerBaseCommand(String name, com.loki.lochat.commands.base.BaseCommand command) {
|
||||
PluginCommand cmd = plugin.getCommand(name);
|
||||
if (cmd != null) {
|
||||
cmd.setExecutor(command);
|
||||
cmd.setTabCompleter(command);
|
||||
commands.put(name, command);
|
||||
} else {
|
||||
plugin.getLogger().warning("[LoChat] Команда '" + name + "' не найдена в plugin.yml!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.commands.custom.*;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Менеджер кастомных команд
|
||||
*/
|
||||
public class CustomCommandManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, CommandExecutor> commands;
|
||||
|
||||
public CustomCommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.commands = new HashMap<>();
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует все кастомные команды
|
||||
*/
|
||||
private void registerCommands() {
|
||||
// Информационные команды
|
||||
commands.put("help", new HelpCommand(plugin));
|
||||
commands.put("rules", new RulesCommand(plugin));
|
||||
|
||||
// Утилиты
|
||||
commands.put("coords", new CoordsCommand(plugin));
|
||||
commands.put("ping", new PingCommand(plugin));
|
||||
commands.put("stats", new StatsCommand(plugin));
|
||||
|
||||
// Чат команды
|
||||
commands.put("me", new MeCommand(plugin));
|
||||
commands.put("sounds", new SoundsCommand(plugin));
|
||||
|
||||
// TODO: Добавить остальные команды
|
||||
// commands.put("website", new WebsiteCommand(plugin));
|
||||
// commands.put("time", new TimeCommand(plugin));
|
||||
// commands.put("list", new ListCommand(plugin));
|
||||
// commands.put("top", new TopCommand(plugin));
|
||||
// commands.put("afk", new AfkCommand(plugin));
|
||||
// commands.put("back", new BackCommand(plugin));
|
||||
// commands.put("broadcast", new BroadcastCommand(plugin));
|
||||
// commands.put("maintenance", new MaintenanceCommand(plugin));
|
||||
// commands.put("motd", new MotdCommand(plugin));
|
||||
// commands.put("tps", new TpsCommand(plugin));
|
||||
// commands.put("memory", new MemoryCommand(plugin));
|
||||
// commands.put("version", new VersionCommand(plugin));
|
||||
// commands.put("reload", new ReloadCommand(plugin));
|
||||
// commands.put("automessage", new AutoMessageCommand(plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует команды в Bukkit
|
||||
*/
|
||||
public void registerBukkitCommands() {
|
||||
for (Map.Entry<String, CommandExecutor> entry : commands.entrySet()) {
|
||||
String commandName = entry.getKey();
|
||||
CommandExecutor executor = entry.getValue();
|
||||
|
||||
if (plugin.getCommand(commandName) != null) {
|
||||
plugin.getCommand(commandName).setExecutor(executor);
|
||||
plugin.getLogger().info("Зарегистрирована кастомная команда: /" + commandName);
|
||||
} else {
|
||||
plugin.getLogger().warning("Не удалось зарегистрировать команду: /" + commandName +
|
||||
" (не найдена в plugin.yml)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить исполнителя команды
|
||||
*/
|
||||
public CommandExecutor getCommandExecutor(String command) {
|
||||
return commands.get(command.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить, является ли команда кастомной
|
||||
*/
|
||||
public boolean isCustomCommand(String command) {
|
||||
return commands.containsKey(command.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить все зарегистрированные команды
|
||||
*/
|
||||
public Map<String, CommandExecutor> getCommands() {
|
||||
return new HashMap<>(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить команды
|
||||
*/
|
||||
public void reload() {
|
||||
commands.clear();
|
||||
registerCommands();
|
||||
registerBukkitCommands();
|
||||
plugin.getLogger().info("Кастомные команды перезагружены!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Команда для управления Discord интеграцией
|
||||
*/
|
||||
public class DiscordCommand implements CommandExecutor, TabCompleter {
|
||||
private final LoChat plugin;
|
||||
private final DiscordIntegration discord;
|
||||
|
||||
public DiscordCommand(LoChat plugin, DiscordIntegration discord) {
|
||||
this.plugin = plugin;
|
||||
this.discord = discord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.discord.admin")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("errors.no-permission")));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sendHelp(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "status":
|
||||
handleStatus(sender);
|
||||
break;
|
||||
case "reload":
|
||||
handleReload(sender);
|
||||
break;
|
||||
case "test":
|
||||
handleTest(sender, args);
|
||||
break;
|
||||
default:
|
||||
sendHelp(sender);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendHelp(CommandSender sender) {
|
||||
sender.sendMessage(ChatFormatter.parse("&6&l=== Discord Integration ==="));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/discord status &7- Показать статус интеграции"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/discord reload &7- Перезагрузить конфигурацию"));
|
||||
sender.sendMessage(ChatFormatter.parse("&e/discord test [message] &7- Отправить тестовое сообщение"));
|
||||
}
|
||||
|
||||
private void handleStatus(CommandSender sender) {
|
||||
if (discord.isEnabled()) {
|
||||
sender.sendMessage(ChatFormatter.parse("&a✓ Discord интеграция включена"));
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse("&c✗ Discord интеграция отключена"));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReload(CommandSender sender) {
|
||||
try {
|
||||
discord.reload();
|
||||
sender.sendMessage(ChatFormatter.parse("&a✓ Discord конфигурация перезагружена"));
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(ChatFormatter.parse("&c✗ Ошибка при перезагрузке: " + e.getMessage()));
|
||||
plugin.getLogger().severe("Error reloading Discord config: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTest(CommandSender sender, String[] args) {
|
||||
if (!discord.isEnabled()) {
|
||||
sender.sendMessage(ChatFormatter.parse("&c✗ Discord интеграция отключена"));
|
||||
return;
|
||||
}
|
||||
|
||||
String message = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : "Тестовое сообщение от " + sender.getName();
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("&e⏳ Отправка тестового сообщения..."));
|
||||
|
||||
// Используем CompletableFuture для асинхронной отправки (Folia-совместимо)
|
||||
java.util.concurrent.CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
discord.sendTestMessage(message, sender.getName());
|
||||
// Возвращаемся в главный поток для отправки сообщения
|
||||
if (sender instanceof org.bukkit.entity.Player player) {
|
||||
player.getScheduler().run(plugin, (task) ->
|
||||
sender.sendMessage(ChatFormatter.parse("&a✓ Тестовое сообщение отправлено в Discord")), null
|
||||
);
|
||||
} else {
|
||||
// Для консоли можно отправить сразу
|
||||
sender.sendMessage(ChatFormatter.parse("&a✓ Тестовое сообщение отправлено в Discord"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (sender instanceof org.bukkit.entity.Player player) {
|
||||
player.getScheduler().run(plugin, (task) ->
|
||||
sender.sendMessage(ChatFormatter.parse("&c✗ Ошибка отправки: " + e.getMessage())), null
|
||||
);
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse("&c✗ Ошибка отправки: " + e.getMessage()));
|
||||
}
|
||||
plugin.getLogger().severe("Discord test error: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.discord.admin")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("status", "reload", "test");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для добавления игрока в список игнорируемых
|
||||
* /ignore <ник>
|
||||
*/
|
||||
public class IgnoreCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/ignore <ник>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target.equals(player)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.self")));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.already-ignored")));
|
||||
return true;
|
||||
}
|
||||
|
||||
ignoreService.addIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.added", "{player}", target.getName())
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /ignorelist - показывает список игнорируемых игроков
|
||||
*/
|
||||
public class IgnoreListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<UUID> ignoredPlayers = ignoreService.getIgnoredPlayers(player.getUniqueId());
|
||||
|
||||
if (ignoredPlayers.isEmpty()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-empty")));
|
||||
return true;
|
||||
}
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-header")));
|
||||
|
||||
for (UUID ignoredUuid : ignoredPlayers) {
|
||||
String playerName = com.loki.lochat.utils.PlayerUtil.getPlayerName(ignoredUuid);
|
||||
if (playerName != null) {
|
||||
player.sendMessage(ChatFormatter.parse("WCEEB• &#FFFFFF" + playerName));
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.list-footer")
|
||||
.replace("{count}", String.valueOf(ignoredPlayers.size()))
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoChatCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
private final LoChat plugin;
|
||||
|
||||
public LoChatCommand(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.admin")) {
|
||||
sender.sendMessage(MM.deserialize(plugin.getMessageConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(MM.deserialize("<#FFD700>LoChat v" + plugin.getPluginMeta().getVersion()));
|
||||
sender.sendMessage(MM.deserialize("<#808080>Команды:"));
|
||||
sender.sendMessage(MM.deserialize("<#FFFF00>/lochat reload</color> <#808080>- Перезагрузить конфиги"));
|
||||
sender.sendMessage(MM.deserialize("<#FFFF00>/lochat commands</color> <#808080>- Список кастомных команд"));
|
||||
sender.sendMessage(MM.deserialize("<#FFFF00>/lochat commands reload</color> <#808080>- Перезагрузить кастомные команды"));
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "reload" -> {
|
||||
plugin.reload();
|
||||
sender.sendMessage(MM.deserialize("<#00FF00>Конфигурация перезагружена!"));
|
||||
}
|
||||
case "commands" -> {
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("reload")) {
|
||||
plugin.getCustomCommandManager().reload();
|
||||
sender.sendMessage(MM.deserialize("<#00FF00>Кастомные команды перезагружены!"));
|
||||
} else {
|
||||
var commands = plugin.getCustomCommandManager().getCommands();
|
||||
sender.sendMessage(MM.deserialize("<#FFD700>Кастомные команды (" + commands.size() + "):"));
|
||||
for (var cmd : commands.values()) {
|
||||
sender.sendMessage(MM.deserialize("<#FFEB3B>/" + cmd.name() + "</color> <#808080>(" + cmd.type() + ")"));
|
||||
if (!cmd.aliases().isEmpty()) {
|
||||
sender.sendMessage(MM.deserialize("<#808080> Алиасы: " + String.join(", ", cmd.aliases())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default ->
|
||||
sender.sendMessage(MM.deserialize("<#FF0000>Неизвестная команда. Используйте /lochat для помощи."));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.admin")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
String input = args[0].toLowerCase();
|
||||
|
||||
for (String cmd : List.of("reload", "commands")) {
|
||||
if (cmd.startsWith(input)) {
|
||||
completions.add(cmd);
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
if (args.length == 2 && args[0].equalsIgnoreCase("commands")) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
String input = args[1].toLowerCase();
|
||||
|
||||
if ("reload".startsWith(input)) {
|
||||
completions.add("reload");
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для отправки личных сообщений
|
||||
* /msg <ник> <сообщение>
|
||||
*/
|
||||
public class MsgCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PMService pmService;
|
||||
private final IgnoreService ignoreService;
|
||||
private final SpyService spyService;
|
||||
|
||||
public MsgCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.pmService = plugin.getServiceRegistry().get(PMService.class);
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfigManager().isPmEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/msg <ник> <сообщение>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.player-offline")));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target.equals(player)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.self")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка игнора
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Собираем сообщение
|
||||
StringBuilder messageBuilder = new StringBuilder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
if (i > 1) messageBuilder.append(" ");
|
||||
messageBuilder.append(args[i]);
|
||||
}
|
||||
String message = messageBuilder.toString();
|
||||
|
||||
// Отправляем сообщения с градиентными именами
|
||||
player.sendMessage(ChatFormatter.formatPmSentNew(
|
||||
plugin.getMessageConfig().getPmFormatSent(),
|
||||
player,
|
||||
target,
|
||||
message
|
||||
));
|
||||
|
||||
target.sendMessage(ChatFormatter.formatPmReceivedNew(
|
||||
plugin.getMessageConfig().getPmFormatReceived(),
|
||||
player,
|
||||
target,
|
||||
message
|
||||
));
|
||||
|
||||
// Звук для получателя
|
||||
if (plugin.getConfigManager().isPmSoundEnabled()) {
|
||||
Sound sound = PlayerUtil.parseSound(plugin.getConfigManager().getPmSoundType(), null);
|
||||
if (sound != null) {
|
||||
target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Шпион
|
||||
spyService.broadcastPM(player, target, message);
|
||||
|
||||
// Сохраняем последнего собеседника
|
||||
pmService.setLastConversation(player.getUniqueId(), target.getUniqueId());
|
||||
pmService.setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
|
||||
// Логирование
|
||||
if (plugin.getConfigManager().isPmLogEnabled()) {
|
||||
plugin.getLogger().info("[PM] " + player.getName() + " -> " + target.getName() + ": " + message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Команда /lmuteblame <модератор> - история мутов выданных модератором
|
||||
*/
|
||||
public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteBlameCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.muteblame")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§cИспользование: /lmuteblame <модератор>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String operatorName = args[0];
|
||||
|
||||
List<MuteData.MuteHistoryEntry> mutes = muteService.getMutesByOperator(operatorName);
|
||||
|
||||
if (mutes.isEmpty()) {
|
||||
sender.sendMessage("§aМодератор §e" + operatorName + " §aне выдавал мутов");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage("§6§l=== Муты выданные " + operatorName + " (" + mutes.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
for (MuteData.MuteHistoryEntry entry : mutes) {
|
||||
// Статус
|
||||
String status;
|
||||
if (entry.unmuted) {
|
||||
status = "§aРазмучен";
|
||||
} else {
|
||||
if (entry.duration == 0) {
|
||||
status = "§cАктивен (навсегда)";
|
||||
} else {
|
||||
long endTime = entry.mutedAt + entry.duration;
|
||||
if (System.currentTimeMillis() > endTime) {
|
||||
status = "§7Истёк";
|
||||
} else {
|
||||
long remaining = endTime - System.currentTimeMillis();
|
||||
status = "§cАктивен (" + muteService.formatTime(remaining) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String durationStr = entry.duration == 0 ? "Навсегда" : muteService.formatTime(entry.duration);
|
||||
|
||||
String playerName = entry.playerName != null ? entry.playerName : "???";
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + status);
|
||||
sender.sendMessage(" §7Дата: §f" + dateFormat.format(new Date(entry.mutedAt)));
|
||||
sender.sendMessage(" §7Длительность: §f" + durationStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (entry.reason != null ? entry.reason : "Не указана"));
|
||||
|
||||
if (entry.unmuted && entry.unmutedBy != null) {
|
||||
sender.sendMessage(" §7Размутил: §f" + entry.unmutedBy);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
if (index > 15) {
|
||||
sender.sendMessage("§7... и ещё " + (mutes.size() - 15) + " записей");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /mute nick [time] [-s] [причина]
|
||||
* Поддержка -s в любом месте до причины
|
||||
*/
|
||||
public class MuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public MuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.mute")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§cИспользование: /mute <ник> [время] [-s] [причина]");
|
||||
sender.sendMessage("§7Время: 1d, 2h, 30m, 60s или 0/perm для перманентного");
|
||||
sender.sendMessage("§7-s - тихий мут (требует право lochat.mute.silent)");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Парсим аргументы
|
||||
String targetName = null;
|
||||
String timeStr = null;
|
||||
boolean silent = false;
|
||||
StringBuilder reasonBuilder = new StringBuilder();
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.equalsIgnoreCase("-s")) {
|
||||
silent = true;
|
||||
} else if (targetName == null) {
|
||||
targetName = arg;
|
||||
} else if (timeStr == null && isTimeFormat(arg)) {
|
||||
timeStr = arg;
|
||||
} else {
|
||||
if (reasonBuilder.length() > 0) reasonBuilder.append(" ");
|
||||
reasonBuilder.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка права на тихий мут
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage("§cУ вас нет права на тихий мут!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Получаем настройки из конфига
|
||||
String defaultReason = plugin.getConfigManager().getString("mute.default-reason", "Нету.");
|
||||
String defaultDuration = plugin.getConfigManager().getString("mute.default-duration", "7d");
|
||||
|
||||
String reason = reasonBuilder.length() > 0 ? reasonBuilder.toString() : defaultReason;
|
||||
|
||||
// Определяем длительность
|
||||
long duration;
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
if (sender instanceof Player player) {
|
||||
duration = muteService.getMaxDuration(player);
|
||||
if (duration == -1) {
|
||||
duration = muteService.parseTime(defaultDuration);
|
||||
}
|
||||
} else {
|
||||
duration = 0; // Консоль = перманентный
|
||||
}
|
||||
} else if (timeStr.equalsIgnoreCase("perm") || timeStr.equals("0")) {
|
||||
duration = 0; // Перманентный
|
||||
} else {
|
||||
duration = muteService.parseTime(timeStr);
|
||||
if (duration < 0) {
|
||||
sender.sendMessage("§cНеверный формат времени! Используйте: 1d, 2h, 30m, 60s, perm");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем право на длительность
|
||||
if (sender instanceof Player player) {
|
||||
if (!muteService.canMuteForDuration(player, duration)) {
|
||||
long maxDur = muteService.getMaxDuration(player);
|
||||
String maxStr = maxDur == 0 ? "перманентный" : muteService.formatTime(maxDur);
|
||||
sender.sendMessage("§cВы не можете мутить на такой срок! Максимум: " + maxStr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ищем игрока
|
||||
UUID targetUUID = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUUID == null) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(targetUUID);
|
||||
String finalTargetName = com.loki.lochat.utils.PlayerUtil.getPlayerName(targetUUID);
|
||||
|
||||
// Проверяем, не замучен ли уже
|
||||
if (muteService.isMuted(targetUUID)) {
|
||||
sender.sendMessage("§cИгрок уже замучен!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Мутим игрока
|
||||
String operatorName = sender.getName();
|
||||
muteService.mute(targetUUID, finalTargetName, duration, reason, operatorName);
|
||||
|
||||
String timeDisplay = duration == 0 ? "навсегда" : muteService.formatTime(duration);
|
||||
|
||||
// Уведомляем отправителя
|
||||
sender.sendMessage("§aИгрок §e" + finalTargetName + " §aзамучен на §e" + timeDisplay);
|
||||
sender.sendMessage("§7Причина: §f" + reason);
|
||||
|
||||
// Уведомляем замученного (если онлайн)
|
||||
if (target != null) {
|
||||
String msgKey = duration == 0 ? "mute.messages.you-muted-permanent" : "mute.messages.you-muted";
|
||||
String msg = plugin.getConfigManager().getString(msgKey,
|
||||
duration == 0 ? "§cВы замучены навсегда! Причина: %reason%" : "§cВы замучены на %duration%! Причина: %reason%");
|
||||
msg = muteService.formatMessage(msg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
target.sendMessage(ChatFormatter.parse(msg));
|
||||
}
|
||||
|
||||
// Broadcast
|
||||
if (silent) {
|
||||
String silentMsg = plugin.getConfigManager().getString("mute.messages.silent-muted",
|
||||
"§8[Тихо] §c%player% §7замучен на §c%duration% §7(%operator%). Причина: %reason%");
|
||||
silentMsg = muteService.formatMessage(silentMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.mute.see-silent")) {
|
||||
p.sendMessage(ChatFormatter.parse(silentMsg));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String broadcastMsg = plugin.getConfigManager().getString("mute.messages.muted",
|
||||
"§c%player% §7был замучен на §c%duration% §7модератором §c%operator%§7. Причина: §c%reason%");
|
||||
broadcastMsg = muteService.formatMessage(broadcastMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isTimeFormat(String str) {
|
||||
if (str.equalsIgnoreCase("perm")) return true;
|
||||
return str.matches("\\d+[dhms]?");
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
} else if (args.length == 2) {
|
||||
completions.add("10m");
|
||||
completions.add("30m");
|
||||
completions.add("1h");
|
||||
completions.add("6h");
|
||||
completions.add("12h");
|
||||
completions.add("1d");
|
||||
completions.add("7d");
|
||||
completions.add("30d");
|
||||
completions.add("perm");
|
||||
completions.add("-s");
|
||||
} else if (args.length == 3) {
|
||||
completions.add("-s");
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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 MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteHistoryCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@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 = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUUID == null) {
|
||||
// Пробуем найти по имени в истории
|
||||
targetUUID = muteService.getUUIDByName(targetName);
|
||||
}
|
||||
|
||||
if (targetUUID == null) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found"));
|
||||
return true;
|
||||
}
|
||||
|
||||
List<MuteData.MuteHistoryEntry> history = muteService.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--) {
|
||||
MuteData.MuteHistoryEntry entry = history.get(i);
|
||||
|
||||
// Статус
|
||||
String status;
|
||||
if (entry.unmuted) {
|
||||
status = "§aРазмучен";
|
||||
} else {
|
||||
// Проверяем, активен ли ещё мут
|
||||
if (entry.duration == 0) {
|
||||
status = "§cАктивен (навсегда)";
|
||||
} else {
|
||||
long endTime = entry.mutedAt + entry.duration;
|
||||
if (System.currentTimeMillis() > endTime) {
|
||||
status = "§7Истёк";
|
||||
} else {
|
||||
long remaining = endTime - System.currentTimeMillis();
|
||||
status = "§cАктивен (" + muteService.formatTime(remaining) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Длительность
|
||||
String durationStr = entry.duration == 0 ? "Навсегда" : muteService.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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 MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@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, MuteData> activeMutes = muteService.getActiveMutes();
|
||||
|
||||
if (activeMutes.isEmpty()) {
|
||||
sender.sendMessage("§aНет активных мутов");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage("§6§l=== Активные муты (" + activeMutes.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
for (Map.Entry<UUID, MuteData> entry : activeMutes.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
MuteData data = entry.getValue();
|
||||
|
||||
// Получаем имя игрока
|
||||
String playerName = data.getPlayerName();
|
||||
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.getEndTime() - System.currentTimeMillis();
|
||||
timeStr = "§e" + muteService.formatTime(remaining);
|
||||
}
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + timeStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (data.getReason() != null ? data.getReason() : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + data.getMutedBy() + " §7(" + dateFormat.format(new Date(data.getMutedAt())) + ")");
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NickCommand implements CommandExecutor, TabCompleter {
|
||||
private final LoChat plugin;
|
||||
private final NickService nickService;
|
||||
|
||||
public NickCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.nickService = plugin.getServiceRegistry().get(NickService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(Component.text("Эта команда только для игроков!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!player.hasPermission("lochat.nick")) {
|
||||
player.sendMessage(Component.text("У вас нет прав на использование этой команды!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
return showCurrentNick(player);
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("reset")) {
|
||||
return resetNick(player);
|
||||
}
|
||||
|
||||
String nickname = String.join(" ", args);
|
||||
return setNick(player, nickname);
|
||||
}
|
||||
|
||||
private boolean showCurrentNick(Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
|
||||
if (currentNick.isPresent()) {
|
||||
Component nickComponent = ChatFormatter.parse(currentNick.get());
|
||||
player.sendMessage(Component.text("Ваш текущий ник: ", NamedTextColor.GRAY)
|
||||
.append(nickComponent));
|
||||
player.sendMessage(Component.text("Используйте ", NamedTextColor.GRAY)
|
||||
.append(Component.text("/nick reset", NamedTextColor.YELLOW))
|
||||
.append(Component.text(" для сброса", NamedTextColor.GRAY)));
|
||||
} else {
|
||||
player.sendMessage(Component.text("У вас нет кастомного ника", NamedTextColor.GRAY));
|
||||
player.sendMessage(Component.text("Используйте ", NamedTextColor.GRAY)
|
||||
.append(Component.text("/nick <ник>", NamedTextColor.YELLOW))
|
||||
.append(Component.text(" для установки", NamedTextColor.GRAY)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean resetNick(Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
|
||||
if (currentNick.isEmpty()) {
|
||||
player.sendMessage(Component.text("У вас нет кастомного ника!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
nickService.resetNickname(player.getUniqueId());
|
||||
player.sendMessage(Component.text("Ваш ник сброшен!", NamedTextColor.GREEN));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setNick(Player player, String nickname) {
|
||||
if (!nickService.isValidNickname(nickname)) {
|
||||
player.sendMessage(Component.text("Невалидный ник!", NamedTextColor.RED));
|
||||
player.sendMessage(Component.text("Требования:", NamedTextColor.GRAY));
|
||||
player.sendMessage(Component.text("• Длина: 3-16 символов", NamedTextColor.GRAY));
|
||||
player.sendMessage(Component.text("• Разрешены: буквы, цифры, _, русские буквы", NamedTextColor.GRAY));
|
||||
player.sendMessage(Component.text("• Поддержка цветов: &#FF0000текст, <gradient:&#FF0000:�FF00>текст</gradient>", NamedTextColor.GRAY));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nickService.isNicknameTaken(nickname)) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
if (currentNick.isEmpty() || !currentNick.get().equals(nickname)) {
|
||||
player.sendMessage(Component.text("Этот ник уже занят!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = nickService.setNickname(player.getUniqueId(), nickname);
|
||||
|
||||
if (success) {
|
||||
Component nickComponent = ChatFormatter.parse(nickname);
|
||||
player.sendMessage(Component.text("Ваш ник изменён на: ", NamedTextColor.GREEN)
|
||||
.append(nickComponent));
|
||||
} else {
|
||||
player.sendMessage(Component.text("Не удалось установить ник!", NamedTextColor.RED));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
String input = args[0].toLowerCase();
|
||||
|
||||
if ("reset".startsWith(input)) {
|
||||
completions.add("reset");
|
||||
}
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
if (currentNick.isPresent()) {
|
||||
completions.add(currentNick.get());
|
||||
}
|
||||
}
|
||||
|
||||
completions.add("&#FF0000Красный");
|
||||
completions.add("�FF00Зелёный");
|
||||
completions.add("Русский_Ник");
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для показа информации об игроке (используется при клике на голову)
|
||||
*/
|
||||
public class PlayerInfoCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PlayerInfoCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length < 2 || !args[0].equals("playerinfo")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String targetName = args[1];
|
||||
Player target = Bukkit.getPlayer(targetName);
|
||||
|
||||
if (target == null || !target.isOnline()) {
|
||||
sender.sendMessage(ChatFormatter.parse("§cИгрок не найден или не в сети"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Показываем информацию об игроке
|
||||
sender.sendMessage(ChatFormatter.parse("§6=== Информация об игроке ==="));
|
||||
sender.sendMessage(ChatFormatter.parse("§eИмя: §f" + target.getName()));
|
||||
sender.sendMessage(ChatFormatter.parse("§eОтображаемое имя: §f" + target.displayName()));
|
||||
sender.sendMessage(ChatFormatter.parse("§eУровень: §f" + target.getLevel()));
|
||||
sender.sendMessage(ChatFormatter.parse("§eЗдоровье: §f" + Math.round(target.getHealth()) + "/20"));
|
||||
sender.sendMessage(ChatFormatter.parse("§eМир: §f" + target.getWorld().getName()));
|
||||
|
||||
// Если есть градиентный модуль, показываем градиентное имя
|
||||
if (plugin.getGradientModule() != null) {
|
||||
String gradientName = plugin.getGradientModule().getFormattedName(target);
|
||||
sender.sendMessage(ChatFormatter.parse("§eГрадиентное имя: " + gradientName));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /unmute nick [-s]
|
||||
*/
|
||||
public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public UnmuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.unmute")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§cИспользование: /unmute <ник> [-s]");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Парсим аргументы
|
||||
String targetName = null;
|
||||
boolean silent = false;
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.equalsIgnoreCase("-s")) {
|
||||
silent = true;
|
||||
} else if (targetName == null) {
|
||||
targetName = arg;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка права на тихий размут
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage("§cУ вас нет права на тихий размут!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ищем игрока
|
||||
UUID targetUUID = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUUID == null) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(targetUUID);
|
||||
String finalTargetName = com.loki.lochat.utils.PlayerUtil.getPlayerName(targetUUID);
|
||||
|
||||
String operatorName = sender.getName();
|
||||
|
||||
// Размучиваем
|
||||
if (muteService.unmute(targetUUID, operatorName)) {
|
||||
sender.sendMessage("§aИгрок §e" + finalTargetName + " §aразмучен");
|
||||
|
||||
// Уведомляем игрока (если онлайн)
|
||||
if (target != null) {
|
||||
target.sendMessage("§aВы были размучены!");
|
||||
}
|
||||
|
||||
// Broadcast
|
||||
if (silent) {
|
||||
String silentMsg = plugin.getConfigManager().getString("mute.messages.silent-unmuted",
|
||||
"§8[Тихо] §a%player% §7размучен (%operator%)");
|
||||
silentMsg = muteService.formatMessage(silentMsg, finalTargetName, operatorName, null, null);
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.mute.see-silent")) {
|
||||
p.sendMessage(ChatFormatter.parse(silentMsg));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String broadcastMsg = plugin.getConfigManager().getString("mute.messages.unmuted",
|
||||
"§a%player% §7был размучен модератором §a%operator%");
|
||||
broadcastMsg = muteService.formatMessage(broadcastMsg, finalTargetName, operatorName, null, null);
|
||||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage("§cИгрок не замучен!");
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else if (args.length == 2) {
|
||||
completions.add("-s");
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
|
|
@ -27,8 +27,6 @@ public class AnnounceCommand implements CommandExecutor {
|
|||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/announce <сообщение>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// /announce reload
|
||||
if (args[0].equalsIgnoreCase("reload") && sender.hasPermission("chat.announce.reload")) {
|
||||
plugin.reload();
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("announce.reloaded")));
|
||||
|
|
@ -36,33 +34,21 @@ public class AnnounceCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
String format = plugin.getConfigManager().getAnnouncementFormat();
|
||||
Component formatted = ChatFormatter.formatAnnouncement(format, message);
|
||||
Component formatted = ChatFormatter.formatAnnouncement(plugin.getConfigManager().getAnnouncementFormat(), message);
|
||||
|
||||
// Отправка в чат
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(formatted);
|
||||
|
||||
// ActionBar (если включен)
|
||||
if (plugin.getConfigManager().isAnnouncementActionBarEnabled()) {
|
||||
if (plugin.getConfigManager().isAnnouncementActionBarEnabled())
|
||||
player.sendActionBar(formatted);
|
||||
}
|
||||
|
||||
// Title (если включен)
|
||||
if (plugin.getConfigManager().isAnnouncementTitleEnabled()) {
|
||||
Title title = Title.title(
|
||||
player.showTitle(Title.title(
|
||||
ChatFormatter.parse(plugin.getConfigManager().getAnnouncementTitleHeader()),
|
||||
ChatFormatter.parse(message),
|
||||
Title.Times.times(
|
||||
Duration.ofMillis(500),
|
||||
Title.Times.times(Duration.ofMillis(500),
|
||||
Duration.ofSeconds(plugin.getConfigManager().getAnnouncementTitleDuration()),
|
||||
Duration.ofMillis(500)
|
||||
)
|
||||
);
|
||||
player.showTitle(title);
|
||||
Duration.ofMillis(500))));
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("[ANNOUNCE] " + sender.getName() + ": " + ChatFormatter.stripTags(message));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
|
|
@ -9,10 +9,6 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для переключения режима шпиона
|
||||
* /chatspy
|
||||
*/
|
||||
public class ChatSpyCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
|
@ -26,14 +22,11 @@ public class ChatSpyCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean enabled = spyService.toggleSpy(player.getUniqueId());
|
||||
String msgKey = enabled ? "spy.enabled" : "spy.disabled";
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(msgKey)));
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(enabled ? "spy.enabled" : "spy.disabled")));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
|
|
@ -19,19 +19,14 @@ public class ClearChatCommand implements CommandExecutor {
|
|||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
// Отправляем пустые строки всем игрокам для очистки чата
|
||||
int lines = plugin.getConfigManager().getAppearanceConfig().getClearLines();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
for (int i = 0; i < com.loki.lochat.utils.Constants.CLEAR_CHAT_LINES; i++) {
|
||||
player.sendMessage("");
|
||||
}
|
||||
|
||||
// Уведомление (если включено)
|
||||
for (int i = 0; i < lines; i++) player.sendMessage("");
|
||||
if (plugin.getConfigManager().isClearChatMessageEnabled()) {
|
||||
String message = plugin.getConfigManager().getClearChatMessage();
|
||||
player.sendMessage(ChatFormatter.parse(message.replace("{player}", sender.getName())));
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getConfigManager().getClearChatMessage().replace("{player}", sender.getName())));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ClearChatConfigCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public ClearChatConfigCommand(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.clearchat.config")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Использование: ẲE9/clearchatconfig <enable|disable|message|status>"));
|
||||
return true;
|
||||
}
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "enable" -> { plugin.getConfigManager().setClearChatMessageEnabled(true); sender.sendMessage(ChatFormatter.parse("⚖C9Сообщение очистки ẲE9включено")); }
|
||||
case "disable" -> { plugin.getConfigManager().setClearChatMessageEnabled(false); sender.sendMessage(ChatFormatter.parse("⚖C9Сообщение очистки &#B798A8отключено")); }
|
||||
case "message" -> {
|
||||
if (args.length < 2) { sender.sendMessage(ChatFormatter.parse("&#CF6679Укажите текст!")); return true; }
|
||||
String msg = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
plugin.getConfigManager().setClearChatMessage(msg);
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Сообщение установлено: &f" + msg));
|
||||
}
|
||||
case "status" -> {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Включено: " + (plugin.getConfigManager().isClearChatMessageEnabled() ? "ẲE9да" : "&#CF6679нет")));
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Текст: &f" + plugin.getConfigManager().getClearChatMessage()));
|
||||
}
|
||||
default -> sender.sendMessage(ChatFormatter.parse("&#CF6679Неизвестный аргумент"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (args.length == 1) return List.of("enable", "disable", "message", "status").stream()
|
||||
.filter(s -> s.startsWith(args[0].toLowerCase())).toList();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class DiscordCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final DiscordIntegration discord;
|
||||
|
||||
public DiscordCommand(LoChat plugin, DiscordIntegration discord) {
|
||||
this.plugin = plugin;
|
||||
this.discord = discord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.discord.admin")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("errors.no-permission")));
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) { sendHelp(sender); return true; }
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "status" -> sender.sendMessage(ChatFormatter.parse(discord.isEnabled() ? "⚖C9Discord ẲE9включён" : "⚖C9Discord &#CF6679отключён"));
|
||||
case "reload" -> {
|
||||
try { discord.reload(); sender.sendMessage(ChatFormatter.parse("⚖C9Discord ẲE9перезагружен")); }
|
||||
catch (Exception e) { sender.sendMessage(ChatFormatter.parse("&#CF6679Ошибка: " + e.getMessage())); }
|
||||
}
|
||||
case "test" -> {
|
||||
if (!discord.isEnabled()) { sender.sendMessage(ChatFormatter.parse("&#CF6679Discord отключён")); return true; }
|
||||
String msg = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : "Тест от " + sender.getName();
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Отправка..."));
|
||||
java.util.concurrent.CompletableFuture.runAsync(() -> {
|
||||
discord.sendTestMessage(msg, sender.getName());
|
||||
if (sender instanceof org.bukkit.entity.Player p)
|
||||
p.getScheduler().run(plugin, t -> sender.sendMessage(ChatFormatter.parse("⚖C9Отправлено ẲE9✓")), null);
|
||||
else sender.sendMessage(ChatFormatter.parse("⚖C9Отправлено ẲE9✓"));
|
||||
});
|
||||
}
|
||||
default -> sendHelp(sender);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendHelp(CommandSender s) {
|
||||
s.sendMessage(ChatFormatter.parse("&#B798A8/discordadmin status|reload|test"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.discord.admin")) return null;
|
||||
if (args.length == 1) return List.of("status", "reload", "test");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
|
@ -23,30 +23,16 @@ public class HubCommand implements CommandExecutor {
|
|||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("general.players-only")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if hub command is enabled
|
||||
if (!plugin.getConfigManager().getBoolean("hub_command.enabled", true)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("general.command-disabled")));
|
||||
return true;
|
||||
}
|
||||
|
||||
String hubServer = plugin.getConfigManager().getString("hub_command.server", "hub");
|
||||
|
||||
// Send player to hub server via BungeeCord
|
||||
sendToServer(player, hubServer);
|
||||
|
||||
String message = plugin.getMessageConfig().get("hub.connecting")
|
||||
.replace("%server%", hubServer);
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendToServer(Player player, String server) {
|
||||
String server = plugin.getConfigManager().getString("hub_command.server", "hub");
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(server);
|
||||
|
||||
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("hub.connecting").replace("%server%", server)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoChatCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public LoChatCommand(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.admin")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatFormatter.parse("ẲE9LoChat &#B798A8v" + plugin.getPluginMeta().getVersion()));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9/lochat reload &#B798A8— перезагрузить конфиги"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9/lochat commands [reload] &#B798A8— кастомные команды"));
|
||||
return true;
|
||||
}
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "reload" -> {
|
||||
plugin.reload();
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Конфигурация ẲE9перезагружена!"));
|
||||
}
|
||||
case "commands" -> {
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("reload")) {
|
||||
plugin.getCustomCommandManager().reload();
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Кастомные команды ẲE9перезагружены!"));
|
||||
} else {
|
||||
var cmds = plugin.getCustomCommandManager().getCommands();
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Кастомные команды ⚖C9(" + cmds.size() + "⚖C9):"));
|
||||
cmds.values().forEach(c -> sender.sendMessage(ChatFormatter.parse("ẲE9/" + c.name() + " &#B798A8(" + c.type() + ")")));
|
||||
}
|
||||
}
|
||||
default -> sender.sendMessage(ChatFormatter.parse("&#CF6679Неизвестная команда"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.admin")) return new ArrayList<>();
|
||||
if (args.length == 1) return List.of("reload", "commands").stream().filter(s -> s.startsWith(args[0].toLowerCase())).toList();
|
||||
if (args.length == 2 && args[0].equalsIgnoreCase("commands")) return List.of("reload");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.loki.lochat.commands.admin;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReloadConfigCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public ReloadConfigCommand(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.admin.reload")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
plugin.reload();
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Все конфигурации ẲE9успешно перезагружены!"));
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Ошибка при перезагрузке: " + e.getMessage()));
|
||||
plugin.getLogger().severe("Ошибка при перезагрузке: " + e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.loki.lochat.commands.base;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Базовый класс для административных команд
|
||||
*/
|
||||
public abstract class AdminCommand extends BaseCommand {
|
||||
|
||||
private final String requiredPermission;
|
||||
|
||||
public AdminCommand(LoChat plugin, String requiredPermission) {
|
||||
super(plugin);
|
||||
this.requiredPermission = requiredPermission;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean executeCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePermission(sender, requiredPermission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return executeAdminCommand(sender, command, label, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнить административную команду
|
||||
*/
|
||||
protected abstract boolean executeAdminCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args);
|
||||
}
|
||||
101
src/main/java/com/loki/lochat/commands/base/BaseCommand.java
Normal file
101
src/main/java/com/loki/lochat/commands/base/BaseCommand.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package com.loki.lochat.commands.base;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
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.util.List;
|
||||
|
||||
/**
|
||||
* Базовый класс для всех команд плагина
|
||||
* Реализует общую логику и предоставляет утилиты
|
||||
*/
|
||||
public abstract class BaseCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
protected final LoChat plugin;
|
||||
|
||||
public BaseCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
try {
|
||||
return executeCommand(sender, command, label, args);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Ошибка в команде " + command.getName() + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
sender.sendMessage("§cПроизошла ошибка при выполнении команды!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнить команду (переопределяется в наследниках)
|
||||
*/
|
||||
protected abstract boolean executeCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args);
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
return getTabCompletions(sender, command, alias, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить автодополнения (переопределяется в наследниках при необходимости)
|
||||
*/
|
||||
protected @Nullable List<String> getTabCompletions(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить права доступа
|
||||
*/
|
||||
protected boolean hasPermission(CommandSender sender, String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить что отправитель - игрок
|
||||
*/
|
||||
protected boolean requirePlayer(CommandSender sender) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить права и отправить сообщение об ошибке если нет прав
|
||||
*/
|
||||
protected boolean requirePermission(CommandSender sender, String permission) {
|
||||
if (!hasPermission(sender, permission)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getNoPermission());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить игрока из CommandSender (с проверкой)
|
||||
*/
|
||||
protected Player getPlayer(CommandSender sender) {
|
||||
return sender instanceof Player ? (Player) sender : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправить сообщение об использовании команды
|
||||
*/
|
||||
protected void sendUsage(CommandSender sender, String usage) {
|
||||
sender.sendMessage("§cИспользование: " + usage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.loki.lochat.commands.base;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Базовый класс для команд, доступных только игрокам
|
||||
*/
|
||||
public abstract class PlayerCommand extends BaseCommand {
|
||||
|
||||
public PlayerCommand(LoChat plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean executeCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePlayer(sender)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
return executePlayerCommand(player, command, label, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнить команду для игрока
|
||||
*/
|
||||
protected abstract boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.chat;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
|
|
@ -11,11 +11,6 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для глобального чата
|
||||
* /g <сообщение> - отправить сообщение в глобальный чат
|
||||
* /globalchat toggle - переключить режим глобального чата
|
||||
*/
|
||||
public class GlobalChatCommand implements CommandExecutor {
|
||||
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
|
@ -30,34 +25,26 @@ public class GlobalChatCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
// /globalchat toggle
|
||||
if (command.getName().equalsIgnoreCase("globalchat")) {
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("toggle")) {
|
||||
boolean enabled = chatService.toggleGlobalChat(player.getUniqueId());
|
||||
String msgKey = enabled ? "global.toggled-on" : "global.toggled-off";
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(msgKey)));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(enabled ? "global.toggled-on" : "global.toggled-off")));
|
||||
} else {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/globalchat toggle")));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// /g <message>
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/g <сообщение>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
chatService.sendGlobalMessage(player, messageComponent);
|
||||
chatService.sendGlobalMessage(player, MM.deserialize(String.join(" ", args)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.chat;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
@ -11,10 +10,6 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для локального чата
|
||||
* /l <сообщение> - отправить сообщение в локальный чат
|
||||
*/
|
||||
public class LocalChatCommand implements CommandExecutor {
|
||||
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
|
@ -29,7 +24,7 @@ public class LocalChatCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -38,12 +33,7 @@ public class LocalChatCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
chatService.sendLocalMessage(player, messageComponent);
|
||||
chatService.sendLocalMessage(player, MM.deserialize(String.join(" ", args)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда координат игрока
|
||||
*/
|
||||
public class CoordsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public CoordsCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
int x = (int) player.getLocation().getX();
|
||||
int y = (int) player.getLocation().getY();
|
||||
int z = (int) player.getLocation().getZ();
|
||||
String world = player.getWorld().getName();
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFA726:#FF9800>Ваши координаты:</gradient>
|
||||
<gradient:#87CEEB:#4682B4>X:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Y:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Z:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Мир:</gradient> <white>%s</white>
|
||||
""", x, y, z, world);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда Discord сервера
|
||||
*/
|
||||
public class DiscordCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public DiscordCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String discordLink = plugin.getConfig().getString("discord.invite-link", "discord.gg/example");
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#7289DA:#5865F2>═══════ Наш Discord ═══════</gradient>
|
||||
<gray>Присоединяйтесь к нашему сообществу!</gray>
|
||||
|
||||
<click:open_url:'https://%s'><gradient:#87CEEB:#4682B4>🔗 %s</gradient></click>
|
||||
|
||||
<gray>В Discord вы можете:</gray>
|
||||
<white>•</white> <gray>Общаться с игроками</gray>
|
||||
<white>•</white> <gray>Получать новости сервера</gray>
|
||||
<white>•</white> <gray>Участвовать в событиях</gray>
|
||||
<white>•</white> <gray>Получать поддержку</gray>
|
||||
""", discordLink, discordLink);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда помощи сервера
|
||||
*/
|
||||
public class HelpCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public HelpCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String message = """
|
||||
<gradient:#FFD700:#FFA500>═══════ Помощь по серверу ═══════</gradient>
|
||||
<gradient:#87CEEB:#4682B4>/rules</gradient> <gray>- правила сервера</gray>
|
||||
<gradient:#87CEEB:#4682B4>/discord</gradient> <gray>- наш Discord</gray>
|
||||
<gradient:#87CEEB:#4682B4>/website</gradient> <gray>- официальный сайт</gray>
|
||||
<gradient:#87CEEB:#4682B4>/coords</gradient> <gray>- ваши координаты</gray>
|
||||
<gradient:#87CEEB:#4682B4>/time</gradient> <gray>- время на сервере</gray>
|
||||
<gradient:#87CEEB:#4682B4>/ping</gradient> <gray>- ваш пинг</gray>
|
||||
<gradient:#87CEEB:#4682B4>/list</gradient> <gray>- список игроков</gray>
|
||||
<gradient:#87CEEB:#4682B4>/me <действие></gradient> <gray>- действие от третьего лица</gray>
|
||||
<gradient:#87CEEB:#4682B4>/msg <игрок> <сообщение></gradient> <gray>- личное сообщение</gray>
|
||||
<gradient:#87CEEB:#4682B4>/reply <сообщение></gradient> <gray>- ответить на ЛС</gray>
|
||||
<gradient:#87CEEB:#4682B4>/ignore <игрок></gradient> <gray>- игнорировать игрока</gray>
|
||||
<gradient:#87CEEB:#4682B4>/color <цвета></gradient> <gray>- градиентный ник</gray>
|
||||
<gradient:#87CEEB:#4682B4>/prefix <текст></gradient> <gray>- кастомный префикс</gray>
|
||||
""";
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда действия от третьего лица
|
||||
*/
|
||||
public class MeCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public MeCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!player.hasPermission("chat.me")) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>У вас нет прав для использования этой команды!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>Использование: /me <действие></red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверяем мут через LoChat
|
||||
if (plugin.getServiceRegistry() != null) {
|
||||
try {
|
||||
MuteService muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
if (muteService.isMuted(player.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>Вы замучены!</red>"));
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Собираем действие из аргументов
|
||||
String action = String.join(" ", args);
|
||||
|
||||
// Получаем отображаемое имя игрока (с градиентом если есть)
|
||||
String displayName = player.getName();
|
||||
if (plugin.getGradientModule() != null) {
|
||||
displayName = plugin.getGradientModule().getFormattedName(player);
|
||||
}
|
||||
|
||||
// Форматируем сообщение действия
|
||||
String message = String.format("<gray>* %s %s</gray>", displayName, action);
|
||||
|
||||
// Отправляем всем игрокам в радиусе (как локальный чат)
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (onlinePlayer.getWorld().equals(player.getWorld()) &&
|
||||
onlinePlayer.getLocation().distance(player.getLocation()) <= radius) {
|
||||
onlinePlayer.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Логируем действие
|
||||
plugin.getLogger().info(String.format("[ME] %s: %s", player.getName(), action));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда пинга игрока
|
||||
*/
|
||||
public class PingCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PingCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Если указан аргумент - показать пинг другого игрока
|
||||
if (args.length > 0) {
|
||||
if (!player.hasPermission("lochat.ping.others")) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>У вас нет прав для просмотра пинга других игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>Игрок не найден или не в сети!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
int ping = target.getPing();
|
||||
String color = getPingColor(ping);
|
||||
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Пинг игрока %s:</gradient> <%s>%dмс</%s>",
|
||||
target.getName(), color, ping, color
|
||||
);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
} else {
|
||||
// Показать свой пинг
|
||||
int ping = player.getPing();
|
||||
String color = getPingColor(ping);
|
||||
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Ваш пинг:</gradient> <%s>%dмс</%s>",
|
||||
color, ping, color
|
||||
);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getPingColor(int ping) {
|
||||
if (ping < 50) return "green";
|
||||
if (ping < 100) return "yellow";
|
||||
if (ping < 200) return "gold";
|
||||
return "red";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда правил сервера
|
||||
*/
|
||||
public class RulesCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public RulesCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String message = """
|
||||
<gradient:#FF6B6B:#FF4757>═══════ Правила сервера ═══════</gradient>
|
||||
<white>1.</white> <gray>Не используйте читы и моды</gray>
|
||||
<white>2.</white> <gray>Уважайте других игроков</gray>
|
||||
<white>3.</white> <gray>Не спамьте в чате</gray>
|
||||
<white>4.</white> <gray>Не стройте рядом с чужими постройками</gray>
|
||||
<white>5.</white> <gray>Не используйте мат и оскорбления</gray>
|
||||
<white>6.</white> <gray>Не рекламируйте другие серверы</gray>
|
||||
<white>7.</white> <gray>Слушайтесь администрацию</gray>
|
||||
<white>8.</white> <gray>Не злоупотребляйте багами</gray>
|
||||
|
||||
<gradient:#FFA726:#FF9800>Нарушение правил карается мутом или баном!</gradient>
|
||||
<gradient:#87CEEB:#4682B4>За помощью обращайтесь к администрации</gradient>
|
||||
""";
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда управления звуками
|
||||
*/
|
||||
public class SoundsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public SoundsCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String playerUUID = player.getUniqueId().toString();
|
||||
// Используем конфиг плагина для хранения настроек
|
||||
boolean currentState = plugin.getConfig().getBoolean("player-settings." + playerUUID + ".sounds-enabled", true);
|
||||
|
||||
if (args.length == 0) {
|
||||
// Показать текущее состояние
|
||||
String status = currentState ? "<green>включены</green>" : "<red>отключены</red>";
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Звуки сейчас %s</gradient>\n" +
|
||||
"<gray>Используйте /sounds on или /sounds off для изменения</gray>",
|
||||
status
|
||||
);
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = args[0].toLowerCase();
|
||||
boolean newState;
|
||||
|
||||
switch (action) {
|
||||
case "on", "включить", "вкл" -> {
|
||||
newState = true;
|
||||
player.sendMessage(ChatFormatter.parse("<green>Звуки включены!</green>"));
|
||||
}
|
||||
case "off", "отключить", "выкл" -> {
|
||||
newState = false;
|
||||
player.sendMessage(ChatFormatter.parse("<red>Звуки отключены!</red>"));
|
||||
}
|
||||
case "toggle", "переключить" -> {
|
||||
newState = !currentState;
|
||||
String status = newState ? "<green>включены</green>" : "<red>отключены</red>";
|
||||
player.sendMessage(ChatFormatter.parse(String.format("Звуки %s!", status)));
|
||||
}
|
||||
default -> {
|
||||
player.sendMessage(ChatFormatter.parse("""
|
||||
<red>Неверное использование!</red>
|
||||
<gray>Доступные команды:</gray>
|
||||
<white>/sounds on</white> <gray>- включить звуки</gray>
|
||||
<white>/sounds off</white> <gray>- отключить звуки</gray>
|
||||
<white>/sounds toggle</white> <gray>- переключить</gray>
|
||||
"""));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем настройку
|
||||
plugin.getConfig().set("player-settings." + playerUUID + ".sounds-enabled", newState);
|
||||
plugin.saveConfig();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда статистики чата
|
||||
*/
|
||||
public class StatsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public StatsCommand(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.stats")) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>У вас нет прав для просмотра статистики!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0) {
|
||||
// Статистика конкретного игрока
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Игрок не найден!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
showPlayerStats(sender, target);
|
||||
} else {
|
||||
// Общая статистика сервера
|
||||
showServerStats(sender);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showPlayerStats(CommandSender sender, Player target) {
|
||||
// Получаем статистику игрока из конфига
|
||||
String uuid = target.getUniqueId().toString();
|
||||
int messagesSent = plugin.getConfig().getInt("player-stats." + uuid + ".messages-sent", 0);
|
||||
int commandsUsed = plugin.getConfig().getInt("player-stats." + uuid + ".commands-used", 0);
|
||||
long playtime = plugin.getConfig().getLong("player-stats." + uuid + ".total-playtime", 0);
|
||||
|
||||
String playtimeFormatted = formatTime(playtime);
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFD700:#FFA500>═══ Статистика игрока %s ═══</gradient>
|
||||
<gradient:#87CEEB:#4682B4>Сообщений отправлено:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Команд использовано:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Время в игре:</gradient> <white>%s</white>
|
||||
<gradient:#87CEEB:#4682B4>Статус:</gradient> <green>В сети</green>
|
||||
<gradient:#87CEEB:#4682B4>Пинг:</gradient> <yellow>%dмс</yellow>
|
||||
""", target.getName(), messagesSent, commandsUsed, playtimeFormatted, target.getPing());
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
private void showServerStats(CommandSender sender) {
|
||||
// Получаем общую статистику сервера
|
||||
int totalMessages = plugin.getConfig().getInt("server-stats.total-messages", 0);
|
||||
int messagesToday = plugin.getConfig().getInt("server-stats.messages-today", 0);
|
||||
int onlinePlayers = Bukkit.getOnlinePlayers().size();
|
||||
int maxPlayers = Bukkit.getMaxPlayers();
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFD700:#FFA500>═══ Статистика сервера ═══</gradient>
|
||||
<gradient:#87CEEB:#4682B4>Всего сообщений:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Сообщений сегодня:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Игроков онлайн:</gradient> <white>%d/%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Активных мутов:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>TPS:</gradient> <green>%.1f</green>
|
||||
""", totalMessages, messagesToday, onlinePlayers, maxPlayers,
|
||||
getActiveMutesCount(), getCurrentTPS());
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
private String formatTime(long milliseconds) {
|
||||
long seconds = milliseconds / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return String.format("%dд %dч %dм", days, hours % 24, minutes % 60);
|
||||
} else if (hours > 0) {
|
||||
return String.format("%dч %dм", hours, minutes % 60);
|
||||
} else {
|
||||
return String.format("%dм", minutes);
|
||||
}
|
||||
}
|
||||
|
||||
private int getActiveMutesCount() {
|
||||
// Получаем количество активных мутов через LoChat
|
||||
if (plugin.getServiceRegistry() != null) {
|
||||
try {
|
||||
MuteService muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
return (int) Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> muteService.isMuted(p.getUniqueId()))
|
||||
.count();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private double getCurrentTPS() {
|
||||
try {
|
||||
return Bukkit.getTPS()[0];
|
||||
} catch (Exception e) {
|
||||
return 20.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.loki.lochat.commands.messaging;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IgnoreCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
if (args.length == 0) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/ignore <ник>"))); return true; }
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound())); return true; }
|
||||
if (target.equals(player)) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.self"))); return true; }
|
||||
if (ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.already-ignored"))); return true; }
|
||||
|
||||
ignoreService.addIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.added", "{player}", target.getName())));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.loki.lochat.commands.messaging;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class IgnoreListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
|
||||
Set<UUID> ignored = ignoreService.getIgnoredPlayers(player.getUniqueId());
|
||||
if (ignored.isEmpty()) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-empty"))); return true; }
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-header")));
|
||||
for (UUID uuid : ignored) {
|
||||
String name = PlayerUtil.getPlayerName(uuid);
|
||||
if (name != null) player.sendMessage(ChatFormatter.parse("⚖C9◆ &f" + name));
|
||||
}
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-footer").replace("{count}", String.valueOf(ignored.size()))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.loki.lochat.commands.messaging;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MsgCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PMService pmService;
|
||||
private final IgnoreService ignoreService;
|
||||
private final SpyService spyService;
|
||||
|
||||
public MsgCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.pmService = plugin.getServiceRegistry().get(PMService.class);
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
if (!plugin.getConfigManager().isPmEnabled()) return true;
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/msg <ник> <сообщение>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.player-offline"))); return true; }
|
||||
if (target.equals(player)) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.self"))); return true; }
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored"))); return true; }
|
||||
if (ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored"))); return true; }
|
||||
|
||||
String message = String.join(" ", java.util.Arrays.copyOfRange(args, 1, args.length));
|
||||
sendPm(player, target, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendPm(Player sender, Player target, String message) {
|
||||
sender.sendMessage(ChatFormatter.formatPmSentNew(plugin.getMessageConfig().getPmFormatSent(), sender, target, message));
|
||||
target.sendMessage(ChatFormatter.formatPmReceivedNew(plugin.getMessageConfig().getPmFormatReceived(), sender, target, message));
|
||||
if (plugin.getConfigManager().isPmSoundEnabled()) {
|
||||
Sound sound = PlayerUtil.parseSound(plugin.getConfigManager().getPmSoundType(), null);
|
||||
if (sound != null) target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
spyService.broadcastPM(sender, target, message);
|
||||
pmService.setLastConversation(sender.getUniqueId(), target.getUniqueId());
|
||||
pmService.setLastConversation(target.getUniqueId(), sender.getUniqueId());
|
||||
if (plugin.getConfigManager().isPmLogEnabled())
|
||||
plugin.getLogger().info("[PM] " + sender.getName() + " -> " + target.getName() + ": " + message);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.messaging;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
|
|
@ -17,10 +17,6 @@ import org.jetbrains.annotations.NotNull;
|
|||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда для ответа на последнее личное сообщение
|
||||
* /reply <сообщение>
|
||||
*/
|
||||
public class ReplyCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
|
@ -38,74 +34,33 @@ public class ReplyCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfigManager().isPmEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfigManager().isPmEnabled()) return true;
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/reply <сообщение>")));
|
||||
return true;
|
||||
}
|
||||
|
||||
Optional<UUID> lastUuidOpt = pmService.getLastConversation(player.getUniqueId());
|
||||
if (lastUuidOpt.isEmpty()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.no-reply")));
|
||||
return true;
|
||||
}
|
||||
Optional<UUID> lastUuid = pmService.getLastConversation(player.getUniqueId());
|
||||
if (lastUuid.isEmpty()) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.no-reply"))); return true; }
|
||||
|
||||
UUID lastUuid = lastUuidOpt.get();
|
||||
Player target = Bukkit.getPlayer(lastUuid);
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.player-offline")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверка игнора
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored")));
|
||||
return true;
|
||||
}
|
||||
Player target = Bukkit.getPlayer(lastUuid.get());
|
||||
if (target == null) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.player-offline"))); return true; }
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored"))); return true; }
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
// Отправляем сообщения с градиентными именами
|
||||
player.sendMessage(ChatFormatter.formatPmSentNew(
|
||||
plugin.getMessageConfig().getPmFormatSent(),
|
||||
player,
|
||||
target,
|
||||
message
|
||||
));
|
||||
|
||||
target.sendMessage(ChatFormatter.formatPmReceivedNew(
|
||||
plugin.getMessageConfig().getPmFormatReceived(),
|
||||
player,
|
||||
target,
|
||||
message
|
||||
));
|
||||
|
||||
// Звук для получателя
|
||||
player.sendMessage(ChatFormatter.formatPmSentNew(plugin.getMessageConfig().getPmFormatSent(), player, target, message));
|
||||
target.sendMessage(ChatFormatter.formatPmReceivedNew(plugin.getMessageConfig().getPmFormatReceived(), player, target, message));
|
||||
if (plugin.getConfigManager().isPmSoundEnabled()) {
|
||||
Sound sound = PlayerUtil.parseSound(plugin.getConfigManager().getPmSoundType(), null);
|
||||
if (sound != null) {
|
||||
target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
if (sound != null) target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Шпион
|
||||
spyService.broadcastPM(player, target, message);
|
||||
|
||||
// Обновляем последнего собеседника
|
||||
pmService.setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
|
||||
// Логирование
|
||||
if (plugin.getConfigManager().isPmLogEnabled()) {
|
||||
if (plugin.getConfigManager().isPmLogEnabled())
|
||||
plugin.getLogger().info("[PM] " + player.getName() + " -> " + target.getName() + ": " + message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.loki.lochat.commands;
|
||||
package com.loki.lochat.commands.messaging;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
|
|
@ -10,10 +10,6 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для удаления игрока из списка игнорируемых
|
||||
* /unignore <ник>
|
||||
*/
|
||||
public class UnignoreCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
|
@ -26,32 +22,15 @@ public class UnignoreCommand implements CommandExecutor {
|
|||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/unignore <ник>")));
|
||||
return true;
|
||||
}
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
if (args.length == 0) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/unignore <ник>"))); return true; }
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.not-ignored")));
|
||||
return true;
|
||||
}
|
||||
if (target == null) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound())); return true; }
|
||||
if (!ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.not-ignored"))); return true; }
|
||||
|
||||
ignoreService.removeIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.removed", "{player}", target.getName())
|
||||
));
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.removed", "{player}", target.getName())));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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;
|
||||
|
||||
public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteBlameCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.muteblame")) { sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission")); return true; }
|
||||
if (args.length < 1) { sender.sendMessage("&#CF6679Использование: /lmuteblame <модератор>"); return true; }
|
||||
|
||||
List<MuteData.MuteHistoryEntry> mutes = muteService.getMutesByOperator(args[0]);
|
||||
if (mutes.isEmpty()) { sender.sendMessage("⚖C9Модератор ẲE9" + args[0] + " ⚖C9не выдавал мутов"); return true; }
|
||||
|
||||
sender.sendMessage("ẲE9▬▬▬▬▬ &#B798A8Муты от ⚖C9" + args[0] + " ⚖C9(" + mutes.size() + "⚖C9) ẲE9▬▬▬▬▬");
|
||||
int idx = 1;
|
||||
for (MuteData.MuteHistoryEntry e : mutes) {
|
||||
if (idx > 15) { sender.sendMessage("&#B798A8...и ещё " + (mutes.size() - 15) + " записей"); break; }
|
||||
String status = e.unmuted ? "⚖C9Размучен" : (e.duration == 0 ? "&#CF6679Активен (навсегда)" :
|
||||
System.currentTimeMillis() > e.mutedAt + e.duration ? "&#B798A8Истёк" : "&#CF6679Активен");
|
||||
sender.sendMessage("⚖C9" + idx + ". &f" + (e.playerName != null ? e.playerName : "???") + " &#B798A8— " + status);
|
||||
sender.sendMessage(" &f" + fmt.format(new Date(e.mutedAt)) + " &#B798A8Длительность: &f" + (e.duration == 0 ? "навсегда" : muteService.formatTime(e.duration)));
|
||||
sender.sendMessage(" &#B798A8Причина: &f" + (e.reason != null ? e.reason : "—"));
|
||||
idx++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) for (Player p : Bukkit.getOnlinePlayers()) if (p.getName().toLowerCase().startsWith(args[0].toLowerCase())) c.add(p.getName());
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.config.HardcodedMessages;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /mute <ник> [время] [-s] [причина]
|
||||
*/
|
||||
public class MuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public MuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
|
||||
HardcodedMessages hm = plugin.getConfigManager().getHardcodedMessages();
|
||||
|
||||
if (!sender.hasPermission("lochat.mute")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getMuteUsage()));
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getMuteTimeHelp()));
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getMuteSilentHelp()));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Парсим аргументы
|
||||
String targetName = null;
|
||||
String timeStr = null;
|
||||
boolean silent = false;
|
||||
StringBuilder reasonBuilder = new StringBuilder();
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.equalsIgnoreCase("-s")) {
|
||||
silent = true;
|
||||
} else if (targetName == null) {
|
||||
targetName = arg;
|
||||
} else if (timeStr == null && isTimeFormat(arg)) {
|
||||
timeStr = arg;
|
||||
} else {
|
||||
if (reasonBuilder.length() > 0) reasonBuilder.append(" ");
|
||||
reasonBuilder.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getNoSilentPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
String reason = reasonBuilder.length() > 0 ? reasonBuilder.toString() : hm.getDefaultMuteReason();
|
||||
|
||||
// Длительность
|
||||
long duration;
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
duration = sender instanceof Player p ? muteService.getMaxDuration(p) : 0;
|
||||
if (duration == -1) duration = muteService.parseTime(hm.getDefaultMuteDuration());
|
||||
} else if (timeStr.equalsIgnoreCase("perm") || timeStr.equals("0")) {
|
||||
duration = 0;
|
||||
} else {
|
||||
duration = muteService.parseTime(timeStr);
|
||||
if (duration < 0) { sender.sendMessage(ChatFormatter.parse(hm.getMuteTimeHelp())); return true; }
|
||||
}
|
||||
|
||||
if (sender instanceof Player p && !muteService.canMuteForDuration(p, duration)) {
|
||||
long max = muteService.getMaxDuration(p);
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Нельзя мутить на такой срок! &#B798A8Максимум: ẲE9" +
|
||||
(max == 0 ? "навсегда" : muteService.formatTime(max))));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID targetUUID = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUUID == null) { sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found")); return true; }
|
||||
if (muteService.isMuted(targetUUID)) { sender.sendMessage(ChatFormatter.parse("&#CF6679Игрок уже замучен!")); return true; }
|
||||
|
||||
Player target = Bukkit.getPlayer(targetUUID);
|
||||
String finalName = com.loki.lochat.utils.PlayerUtil.getPlayerName(targetUUID);
|
||||
String op = sender.getName();
|
||||
|
||||
muteService.mute(targetUUID, finalName, duration, reason, op);
|
||||
String timeDisplay = duration == 0 ? "навсегда" : muteService.formatTime(duration);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Игрок ẲE9" + finalName + " ⚖C9замучен на ẲE9" + timeDisplay));
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Причина: &f" + reason));
|
||||
|
||||
if (target != null) {
|
||||
String youMsg = duration == 0
|
||||
? hm.getMutedPermanent().replace("{reason}", reason)
|
||||
: hm.getMutedMessage().replace("{reason}", reason).replace("{time}", timeDisplay);
|
||||
target.sendMessage(ChatFormatter.parse(youMsg));
|
||||
}
|
||||
|
||||
if (silent) {
|
||||
String msgText = hm.getPlayerMutedSilent().replace("{player}", finalName).replace("{time}", timeDisplay);
|
||||
for (Player p : Bukkit.getOnlinePlayers()) if (p.hasPermission("lochat.mute.see-silent")) p.sendMessage(ChatFormatter.parse(msgText));
|
||||
} else {
|
||||
String broadcastMsg = (duration == 0 ? hm.getPlayerMutedPermanent() : hm.getPlayerMuted())
|
||||
.replace("{player}", finalName).replace("{time}", timeDisplay).replace("{reason}", reason);
|
||||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isTimeFormat(String s) {
|
||||
return s.equalsIgnoreCase("perm") || s.matches("\\d+[dhms]?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
if (p.getName().toLowerCase().startsWith(args[0].toLowerCase())) c.add(p.getName());
|
||||
} else if (args.length == 2) {
|
||||
c.addAll(List.of("10m", "30m", "1h", "6h", "12h", "1d", "7d", "30d", "perm", "-s"));
|
||||
} else if (args.length == 3) {
|
||||
c.add("-s");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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;
|
||||
|
||||
public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteHistoryCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@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("&#CF6679Использование: /lmutehistory <ник>"); return true; }
|
||||
|
||||
UUID uuid = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(args[0]);
|
||||
if (uuid == null) uuid = muteService.getUUIDByName(args[0]);
|
||||
if (uuid == null) { sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found")); return true; }
|
||||
|
||||
List<MuteData.MuteHistoryEntry> history = muteService.getPlayerHistory(uuid);
|
||||
if (history.isEmpty()) { sender.sendMessage("⚖C9У ẲE9" + args[0] + " ⚖C9нет истории мутов"); return true; }
|
||||
|
||||
sender.sendMessage("ẲE9▬▬▬▬▬ &#B798A8История мутов ⚖C9" + args[0] + " ⚖C9(" + history.size() + "⚖C9) ẲE9▬▬▬▬▬");
|
||||
int idx = 1;
|
||||
for (int i = history.size() - 1; i >= 0 && idx <= 10; i--, idx++) {
|
||||
MuteData.MuteHistoryEntry e = history.get(i);
|
||||
String status = e.unmuted ? "⚖C9Размучен" : (e.duration == 0 ? "&#CF6679Активен (навсегда)" :
|
||||
System.currentTimeMillis() > e.mutedAt + e.duration ? "&#B798A8Истёк" : "&#CF6679Активен (" + muteService.formatTime(e.mutedAt + e.duration - System.currentTimeMillis()) + ")");
|
||||
sender.sendMessage("⚖C9" + idx + ". &f" + fmt.format(new Date(e.mutedAt)) + " &#B798A8— " + status);
|
||||
sender.sendMessage(" &#B798A8Длительность: &f" + (e.duration == 0 ? "навсегда" : muteService.formatTime(e.duration)));
|
||||
sender.sendMessage(" &#B798A8Причина: &f" + (e.reason != null ? e.reason : "—") + " &#B798A8Выдал: ẲE9" + e.mutedBy);
|
||||
if (e.unmuted && e.unmutedBy != null)
|
||||
sender.sendMessage(" &#B798A8Размутил: ẲE9" + e.unmutedBy + " &f(" + fmt.format(new Date(e.unmutedAt)) + ")");
|
||||
}
|
||||
if (history.size() > 10) sender.sendMessage("&#B798A8...и ещё " + (history.size() - 10) + " записей");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) for (Player p : Bukkit.getOnlinePlayers()) if (p.getName().toLowerCase().startsWith(args[0].toLowerCase())) c.add(p.getName());
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
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;
|
||||
|
||||
public class MuteListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@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, MuteData> mutes = muteService.getActiveMutes();
|
||||
if (mutes.isEmpty()) { sender.sendMessage("⚖C9Нет активных мутов"); return true; }
|
||||
|
||||
sender.sendMessage("ẲE9▬▬▬▬▬ &#B798A8Активные муты ⚖C9(" + mutes.size() + "⚖C9) ẲE9▬▬▬▬▬");
|
||||
int i = 1;
|
||||
for (Map.Entry<UUID, MuteData> e : mutes.entrySet()) {
|
||||
MuteData d = e.getValue();
|
||||
String name = d.getPlayerName() != null ? d.getPlayerName() : Bukkit.getOfflinePlayer(e.getKey()).getName();
|
||||
String time = d.isPermanent() ? "&#CF6679навсегда" : "ẲE9" + muteService.formatTime(d.getEndTime() - System.currentTimeMillis());
|
||||
sender.sendMessage("⚖C9" + i + ". &f" + name + " &#B798A8— " + time);
|
||||
sender.sendMessage(" &#B798A8Причина: &f" + (d.getReason() != null ? d.getReason() : "—"));
|
||||
sender.sendMessage(" &#B798A8Выдал: ẲE9" + d.getMutedBy() + " &#B798A8(" + fmt.format(new Date(d.getMutedAt())) + ")");
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public UnmuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.unmute")) { sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission")); return true; }
|
||||
if (args.length < 1) { sender.sendMessage(ChatFormatter.parse("&#CF6679Использование: /unmute <ник> [-s]")); return true; }
|
||||
|
||||
String targetName = null;
|
||||
boolean silent = false;
|
||||
for (String a : args) {
|
||||
if (a.equalsIgnoreCase("-s")) silent = true;
|
||||
else if (targetName == null) targetName = a;
|
||||
}
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoSilentUnmutePermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID uuid = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (uuid == null) { sender.sendMessage(plugin.getMessageConfig().getComponent("errors.player-not-found")); return true; }
|
||||
|
||||
String name = com.loki.lochat.utils.PlayerUtil.getPlayerName(uuid);
|
||||
Player target = Bukkit.getPlayer(uuid);
|
||||
String op = sender.getName();
|
||||
|
||||
if (muteService.unmute(uuid, op)) {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9Игрок ẲE9" + name + " ⚖C9размучен"));
|
||||
if (target != null) target.sendMessage(ChatFormatter.parse("⚖C9Вы были ẲE9размучены!"));
|
||||
if (silent) {
|
||||
String msg = "&#B798A8[⚖C9ТИХО&#B798A8] ẲE9" + name + " &#B798A8размучен ⚖C9(" + op + ")";
|
||||
for (Player p : Bukkit.getOnlinePlayers()) if (p.hasPermission("lochat.mute.see-silent")) p.sendMessage(ChatFormatter.parse(msg));
|
||||
} else {
|
||||
Bukkit.broadcast(ChatFormatter.parse("⚖C9Игрок ẲE9" + name + " ⚖C9размучен модератором ẲE9" + op));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getPlayerNotMutedSimple()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) for (Player p : Bukkit.getOnlinePlayers()) if (p.getName().toLowerCase().startsWith(args[0].toLowerCase())) c.add(p.getName());
|
||||
else if (args.length == 2) c.add("-s");
|
||||
return c;
|
||||
}
|
||||
}
|
||||
92
src/main/java/com/loki/lochat/commands/nick/NickCommand.java
Normal file
92
src/main/java/com/loki/lochat/commands/nick/NickCommand.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package com.loki.lochat.commands.nick;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NickCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final NickService nickService;
|
||||
|
||||
public NickCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.nickService = plugin.getServiceRegistry().get(NickService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(Component.text(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly(), NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
if (!player.hasPermission("lochat.nick")) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
var nick = nickService.getNickname(player.getUniqueId());
|
||||
if (nick.isPresent()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.current", "{nick}", nick.get())));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.reset-usage")));
|
||||
} else {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.no-nick")));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.set-usage")));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("reset")) {
|
||||
if (nickService.getNickname(player.getUniqueId()).isEmpty()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.no-nick")));
|
||||
return true;
|
||||
}
|
||||
nickService.resetNickname(player.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.reset-success")));
|
||||
return true;
|
||||
}
|
||||
|
||||
String nickname = String.join(" ", args);
|
||||
if (!nickService.isValidNickname(nickname)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.invalid")));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.requirements-header")));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.requirements-length")));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.requirements-chars")));
|
||||
return true;
|
||||
}
|
||||
if (nickService.isNicknameTaken(nickname)) {
|
||||
var cur = nickService.getNickname(player.getUniqueId());
|
||||
if (cur.isEmpty() || !cur.get().equals(nickname)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.taken")));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (nickService.setNickname(player.getUniqueId(), nickname)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.set-success", "{nick}", nickname)));
|
||||
} else {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("nick.error")));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
if ("reset".startsWith(args[0].toLowerCase())) c.add("reset");
|
||||
if (sender instanceof Player p) nickService.getNickname(p.getUniqueId()).ifPresent(c::add);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.loki.lochat.commands.nick;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PlayerInfoCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PlayerInfoCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length < 1) return false;
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Игрок не найден или не в сети"));
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("ẲE9▬▬▬▬▬ &#B798A8" + target.getName() + " ẲE9▬▬▬▬▬"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9❤ &fЗдоровье: ẲE9" + Math.round(target.getHealth()) + "/20"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9✦ &fРежим: ẲE9" + target.getGameMode().name().toLowerCase()));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9◆ &fМир: ẲE9" + target.getWorld().getName()));
|
||||
if (plugin.getGradientModule() != null) {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9⚡ &fГрадиент: " + plugin.getGradientModule().getFormattedName(target)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
61
src/main/java/com/loki/lochat/commands/rp/DoCommand.java
Normal file
61
src/main/java/com/loki/lochat/commands/rp/DoCommand.java
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package com.loki.lochat.commands.rp;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.commands.base.PlayerCommand;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* /do <описание сцены>
|
||||
* Описывает событие в мире от третьего лица.
|
||||
* Формат: [Сцена] описание (Игрок)
|
||||
*/
|
||||
public class DoCommand extends PlayerCommand {
|
||||
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
public DoCommand(LoChat plugin) {
|
||||
super(plugin);
|
||||
this.cooldownService = plugin.getServiceRegistry().get(CooldownService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePermission(player, "lochat.rp.do")) return true;
|
||||
|
||||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.do", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_do", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_do", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sendUsage(player, "/do <описание сцены>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String description = String.join(" ", args);
|
||||
String format = plugin.getConfigManager().getString("rp.do-format",
|
||||
"<dark_gray>[</dark_gray><gray>Сцена</gray><dark_gray>]</dark_gray> <white>{description}</white> <dark_gray>({player})</dark_gray>");
|
||||
|
||||
String displayName = RpUtil.getDisplayName(plugin, player);
|
||||
Component message = ChatFormatter.parse(
|
||||
format.replace("{player}", displayName)
|
||||
.replace("{description}", description)
|
||||
);
|
||||
|
||||
int radius = plugin.getConfigManager().getInt("rp.radius", 100);
|
||||
RpUtil.sendToRadius(player, message, radius);
|
||||
|
||||
if (cooldown > 0) cooldownService.setCooldown(player.getUniqueId(), "rp_do");
|
||||
plugin.getLogger().info("[DO] " + player.getName() + ": " + description);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
61
src/main/java/com/loki/lochat/commands/rp/MeCommand.java
Normal file
61
src/main/java/com/loki/lochat/commands/rp/MeCommand.java
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package com.loki.lochat.commands.rp;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.commands.base.PlayerCommand;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* /me <действие>
|
||||
* Видят игроки в радиусе rp.radius блоков.
|
||||
* Формат: * ИгрокИмя действие
|
||||
*/
|
||||
public class MeCommand extends PlayerCommand {
|
||||
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
public MeCommand(LoChat plugin) {
|
||||
super(plugin);
|
||||
this.cooldownService = plugin.getServiceRegistry().get(CooldownService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePermission(player, "lochat.rp.me")) return true;
|
||||
|
||||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.me", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_me", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_me", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sendUsage(player, "/me <действие>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = String.join(" ", args);
|
||||
String format = plugin.getConfigManager().getString("rp.me-format",
|
||||
"<gray>* </gray><white>{player}</white><gray> {action}</gray>");
|
||||
|
||||
String displayName = RpUtil.getDisplayName(plugin, player);
|
||||
Component message = ChatFormatter.parse(
|
||||
format.replace("{player}", displayName)
|
||||
.replace("{action}", action)
|
||||
);
|
||||
|
||||
int radius = plugin.getConfigManager().getInt("rp.radius", 100);
|
||||
RpUtil.sendToRadius(player, message, radius);
|
||||
|
||||
if (cooldown > 0) cooldownService.setCooldown(player.getUniqueId(), "rp_me");
|
||||
plugin.getLogger().info("[ME] " + player.getName() + ": " + action);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
89
src/main/java/com/loki/lochat/commands/rp/RollCommand.java
Normal file
89
src/main/java/com/loki/lochat/commands/rp/RollCommand.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package com.loki.lochat.commands.rp;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.commands.base.PlayerCommand;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* /roll [максимум]
|
||||
* Бросает кубик от 1 до максимума (по умолчанию 100).
|
||||
* Формат: * ИгрокИмя бросает кубик и получает N из M
|
||||
*/
|
||||
public class RollCommand extends PlayerCommand implements TabCompleter {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int DEFAULT_MAX = 100;
|
||||
private static final int ABSOLUTE_MAX = 1_000_000;
|
||||
|
||||
public RollCommand(LoChat plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePermission(player, "lochat.rp.roll")) return true;
|
||||
|
||||
int max = DEFAULT_MAX;
|
||||
|
||||
if (args.length >= 1) {
|
||||
try {
|
||||
max = Integer.parseInt(args[0]);
|
||||
if (max < 2) {
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getConfigManager().getString("rp.roll-min-error",
|
||||
"<red>Минимальное значение кубика: 2</red>")));
|
||||
return true;
|
||||
}
|
||||
if (max > ABSOLUTE_MAX) {
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getConfigManager().getString("rp.roll-max-error",
|
||||
"<red>Максимальное значение кубика: 1000000</red>")));
|
||||
return true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getConfigManager().getString("rp.roll-invalid-number",
|
||||
"<red>Укажите число: /roll [максимум]</red>")));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int result = RANDOM.nextInt(max) + 1; // 1..max включительно
|
||||
|
||||
String format = plugin.getConfigManager().getString("rp.roll-format",
|
||||
"<gray>* </gray><white>{player}</white><gray> бросает кубик и получает </gray><yellow><bold>{result}</bold></yellow><gray> из </gray><yellow>{max}</yellow>");
|
||||
|
||||
String displayName = RpUtil.getDisplayName(plugin, player);
|
||||
Component message = ChatFormatter.parse(
|
||||
format.replace("{player}", displayName)
|
||||
.replace("{result}", String.valueOf(result))
|
||||
.replace("{max}", String.valueOf(max))
|
||||
);
|
||||
|
||||
int radius = plugin.getConfigManager().getInt("rp.radius", 100);
|
||||
RpUtil.sendToRadius(player, message, radius);
|
||||
|
||||
plugin.getLogger().info("[ROLL] " + player.getName() + ": " + result + "/" + max);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull org.bukkit.command.CommandSender sender,
|
||||
@NotNull Command command, @NotNull String alias,
|
||||
@NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
return List.of("6", "10", "20", "100");
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
51
src/main/java/com/loki/lochat/commands/rp/RpUtil.java
Normal file
51
src/main/java/com/loki/lochat/commands/rp/RpUtil.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package com.loki.lochat.commands.rp;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.util.DistanceUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Вспомогательные методы для RP-команд
|
||||
*/
|
||||
public final class RpUtil {
|
||||
|
||||
private RpUtil() {}
|
||||
|
||||
/**
|
||||
* Отправляет компонент всем игрокам в радиусе, включая отправителя.
|
||||
*/
|
||||
public static void sendToRadius(Player sender, Component message, int radius) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
player.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает отображаемое имя игрока — градиентное если модуль включён,
|
||||
* иначе обычное. Возвращает plain-string для подстановки в шаблон.
|
||||
*/
|
||||
public static String getDisplayName(LoChat plugin, Player player) {
|
||||
try {
|
||||
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
||||
return plugin.getGradientModule().getFormattedName(player);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
// Пробуем взять displayName (может содержать градиент от NickService)
|
||||
Component displayName = player.displayName();
|
||||
String plain = PlainTextComponentSerializer.plainText().serialize(displayName);
|
||||
// Если displayName == обычное имя — возвращаем его напрямую (без потери цветов не нужно)
|
||||
if (plain.equals(player.getName())) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
// Если displayName содержит кастомный ник — сериализуем обратно в MiniMessage строку
|
||||
// чтобы она правильно спарсилась через ChatFormatter.parse()
|
||||
return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(displayName);
|
||||
}
|
||||
}
|
||||
71
src/main/java/com/loki/lochat/commands/rp/TryCommand.java
Normal file
71
src/main/java/com/loki/lochat/commands/rp/TryCommand.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package com.loki.lochat.commands.rp;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.commands.base.PlayerCommand;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* /try <действие>
|
||||
* Случайный исход: успех или провал (50/50).
|
||||
* Формат: * ИгрокИмя пытается действие... [Успех! / Провал!]
|
||||
*/
|
||||
public class TryCommand extends PlayerCommand {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
public TryCommand(LoChat plugin) {
|
||||
super(plugin);
|
||||
this.cooldownService = plugin.getServiceRegistry().get(CooldownService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!requirePermission(player, "lochat.rp.try")) return true;
|
||||
|
||||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.try", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_try", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_try", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
sendUsage(player, "/try <действие>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = String.join(" ", args);
|
||||
boolean success = RANDOM.nextBoolean();
|
||||
|
||||
String resultKey = success ? "rp.try-success" : "rp.try-fail";
|
||||
String resultDefault = success ? "<green>[Успех!]</green>" : "<red>[Провал!]</red>";
|
||||
String resultTag = plugin.getConfigManager().getString(resultKey, resultDefault);
|
||||
|
||||
String format = plugin.getConfigManager().getString("rp.try-format",
|
||||
"<gray>* </gray><white>{player}</white><gray> пытается {action}...</gray> {result}");
|
||||
|
||||
String displayName = RpUtil.getDisplayName(plugin, player);
|
||||
Component message = ChatFormatter.parse(
|
||||
format.replace("{player}", displayName)
|
||||
.replace("{action}", action)
|
||||
.replace("{result}", resultTag)
|
||||
);
|
||||
|
||||
int radius = plugin.getConfigManager().getInt("rp.radius", 100);
|
||||
RpUtil.sendToRadius(player, message, radius);
|
||||
|
||||
if (cooldown > 0) cooldownService.setCooldown(player.getUniqueId(), "rp_try");
|
||||
plugin.getLogger().info("[TRY] " + player.getName() + ": " + action + " -> " + (success ? "SUCCESS" : "FAIL"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
282
src/main/java/com/loki/lochat/config/AppearanceConfig.java
Normal file
282
src/main/java/com/loki/lochat/config/AppearanceConfig.java
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Конфигурация внешнего вида чата
|
||||
*/
|
||||
public class AppearanceConfig {
|
||||
private final Plugin plugin;
|
||||
private YamlConfiguration config;
|
||||
private final Map<String, String> emojiCache = new HashMap<>();
|
||||
|
||||
public AppearanceConfig(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
// Не вызываем loadConfig() в конструкторе для избежания this-escape
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать конфигурацию (вызывать после создания объекта)
|
||||
*/
|
||||
public void init() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить конфигурацию
|
||||
*/
|
||||
public void loadConfig() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config/chat-appearance.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("config/chat-appearance.yml", false);
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
loadEmojiCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить конфигурацию
|
||||
*/
|
||||
public void reload() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить кэш эмодзи
|
||||
*/
|
||||
private void loadEmojiCache() {
|
||||
emojiCache.clear();
|
||||
if (config.isConfigurationSection("emojis")) {
|
||||
for (String key : config.getConfigurationSection("emojis").getKeys(false)) {
|
||||
emojiCache.put(key, config.getString("emojis." + key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== ПРЕФИКСЫ ==========
|
||||
|
||||
/**
|
||||
* Получить эмодзи для глобального чата
|
||||
*/
|
||||
public String getGlobalEmoji() {
|
||||
return config.getString("prefixes.global.emoji", "🌍");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить текст префикса глобального чата
|
||||
*/
|
||||
public String getGlobalText() {
|
||||
return config.getString("prefixes.global.text", "GLOBAL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвета градиента для глобального чата
|
||||
*/
|
||||
public List<String> getGlobalColors() {
|
||||
return config.getStringList("prefixes.global.colors");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить разделитель для глобального чата
|
||||
*/
|
||||
public String getGlobalSeparatorText() {
|
||||
return config.getString("prefixes.global.separator.text", " ▶ ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет разделителя для глобального чата
|
||||
*/
|
||||
public String getGlobalSeparatorColor() {
|
||||
return config.getString("prefixes.global.separator.color", "#FFD700");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить эмодзи для локального чата
|
||||
*/
|
||||
public String getLocalEmoji() {
|
||||
return config.getString("prefixes.local.emoji", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить текст префикса локального чата
|
||||
*/
|
||||
public String getLocalText() {
|
||||
return config.getString("prefixes.local.text", "LOCAL");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвета градиента для локального чата
|
||||
*/
|
||||
public List<String> getLocalColors() {
|
||||
return config.getStringList("prefixes.local.colors");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить разделитель для локального чата
|
||||
*/
|
||||
public String getLocalSeparatorText() {
|
||||
return config.getString("prefixes.local.separator.text", " ▶ ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет разделителя для локального чата
|
||||
*/
|
||||
public String getLocalSeparatorColor() {
|
||||
return config.getString("prefixes.local.separator.color", "#FFFFFF");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет текста сообщений в глобальном чате (дефолт)
|
||||
*/
|
||||
public String getGlobalMessageColor() {
|
||||
return config.getString("prefixes.global.message-color", "#FFFFFF");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет текста сообщений в локальном чате (дефолт)
|
||||
*/
|
||||
public String getLocalMessageColor() {
|
||||
return config.getString("prefixes.local.message-color", "#FFFFFF");
|
||||
}
|
||||
|
||||
// ========== HOVER ЭФФЕКТЫ ==========
|
||||
|
||||
/**
|
||||
* Включены ли hover эффекты
|
||||
*/
|
||||
public boolean isHoverEnabled() {
|
||||
return config.getBoolean("hover.enabled", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить формат hover подсказки
|
||||
*/
|
||||
public List<String> getHoverFormat() {
|
||||
return config.getStringList("hover.format");
|
||||
}
|
||||
|
||||
// ========== ЭМОДЗИ ==========
|
||||
|
||||
/**
|
||||
* Получить все эмодзи замены
|
||||
*/
|
||||
public Map<String, String> getEmojis() {
|
||||
return new HashMap<>(emojiCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить замену для эмодзи
|
||||
*/
|
||||
public String getEmoji(String key) {
|
||||
return emojiCache.get(key);
|
||||
}
|
||||
|
||||
// ========== НАСТРОЙКИ ЧАТА ==========
|
||||
|
||||
/**
|
||||
* Получить радиус локального чата
|
||||
*/
|
||||
public int getLocalRadius() {
|
||||
return config.getInt("chat.local_radius", 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить количество строк для очистки чата
|
||||
*/
|
||||
public int getClearLines() {
|
||||
return config.getInt("chat.clear_lines", 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить минимальную длину сообщения
|
||||
*/
|
||||
public int getMinMessageLength() {
|
||||
return config.getInt("chat.min_message_length", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить максимальную длину сообщения
|
||||
*/
|
||||
public int getMaxMessageLength() {
|
||||
return config.getInt("chat.max_message_length", 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет "никто не услышал"
|
||||
*/
|
||||
public String getNobodyHeardColor() {
|
||||
return config.getString("chat.colors.nobody_heard", "#FFA726");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет ошибки
|
||||
*/
|
||||
public String getErrorColor() {
|
||||
return config.getString("chat.colors.error", "#FF6B6B");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет успеха
|
||||
*/
|
||||
public String getSuccessColor() {
|
||||
return config.getString("chat.colors.success", "#6BCB77");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет предупреждения
|
||||
*/
|
||||
public String getWarningColor() {
|
||||
return config.getString("chat.colors.warning", "#FFA726");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет информации
|
||||
*/
|
||||
public String getInfoColor() {
|
||||
return config.getString("chat.colors.info", "#87CEEB");
|
||||
}
|
||||
|
||||
// ========== DISCORD ==========
|
||||
|
||||
/**
|
||||
* Получить эмодзи для Discord событий
|
||||
*/
|
||||
public String getDiscordEmoji(String event) {
|
||||
return config.getString("discord.emojis." + event, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить URL аватара по умолчанию
|
||||
*/
|
||||
public String getDefaultAvatarUrl() {
|
||||
return config.getString("discord.avatar_urls.default", "https://mc-heads.net/avatar/minecraft/64");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить URL аватара игрока
|
||||
*/
|
||||
public String getPlayerAvatarUrl() {
|
||||
return config.getString("discord.avatar_urls.player", "https://mc-heads.net/avatar/{player}/64");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить цвет embed по умолчанию
|
||||
*/
|
||||
public String getDefaultEmbedColor() {
|
||||
return config.getString("discord.default_embed_color", "5865F2");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить заголовок события Discord
|
||||
*/
|
||||
public String getDiscordEventTitle(String event) {
|
||||
return config.getString("discord.event_titles." + event, "");
|
||||
}
|
||||
}
|
||||
|
|
@ -10,16 +10,40 @@ public class ConfigManager {
|
|||
|
||||
private final LoChat plugin;
|
||||
private FileConfiguration config;
|
||||
private AppearanceConfig appearanceConfig;
|
||||
private HardcodedMessages hardcodedMessages;
|
||||
|
||||
public ConfigManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.saveDefaultConfig();
|
||||
this.config = plugin.getConfig();
|
||||
|
||||
// Инициализируем новые конфигурации
|
||||
this.appearanceConfig = new AppearanceConfig(plugin);
|
||||
this.hardcodedMessages = new HardcodedMessages(plugin);
|
||||
|
||||
// Инициализируем их после создания
|
||||
this.appearanceConfig.init();
|
||||
this.hardcodedMessages.init();
|
||||
|
||||
// Проверяем версию конфига и обновляем при необходимости
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию внешнего вида
|
||||
*/
|
||||
public AppearanceConfig getAppearanceConfig() {
|
||||
return appearanceConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию хардкодированных сообщений
|
||||
*/
|
||||
public HardcodedMessages getHardcodedMessages() {
|
||||
return hardcodedMessages;
|
||||
}
|
||||
|
||||
private void updateConfig() {
|
||||
int currentVersion = config.getInt("config-version", 1);
|
||||
int requiredVersion = 2; // Текущая версия конфига
|
||||
|
|
@ -81,6 +105,10 @@ public class ConfigManager {
|
|||
public void reload() {
|
||||
plugin.reloadConfig();
|
||||
this.config = plugin.getConfig();
|
||||
|
||||
// Перезагружаем новые конфигурации
|
||||
this.appearanceConfig.reload();
|
||||
this.hardcodedMessages.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -145,7 +173,7 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public int getLocalRadius() {
|
||||
return config.getInt("chat.local.radius", com.loki.lochat.utils.Constants.DEFAULT_LOCAL_RADIUS);
|
||||
return appearanceConfig.getLocalRadius();
|
||||
}
|
||||
|
||||
public String getLocalPrefix() {
|
||||
|
|
|
|||
261
src/main/java/com/loki/lochat/config/HardcodedMessages.java
Normal file
261
src/main/java/com/loki/lochat/config/HardcodedMessages.java
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Конфигурация хардкодированных сообщений
|
||||
*/
|
||||
public class HardcodedMessages {
|
||||
private final Plugin plugin;
|
||||
private YamlConfiguration config;
|
||||
|
||||
public HardcodedMessages(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
// Не вызываем loadConfig() в конструкторе для избежания this-escape
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать конфигурацию (вызывать после создания объекта)
|
||||
*/
|
||||
public void init() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить конфигурацию
|
||||
*/
|
||||
public void loadConfig() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config/hardcoded-messages.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("config/hardcoded-messages.yml", false);
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить конфигурацию
|
||||
*/
|
||||
public void reload() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
// ========== КОМАНДЫ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение об использовании команды мута
|
||||
*/
|
||||
public String getMuteUsage() {
|
||||
return config.getString("commands.mute.usage", "§cИспользование: /mute <ник> [время] [-s] [причина]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить помощь по времени мута
|
||||
*/
|
||||
public String getMuteTimeHelp() {
|
||||
return config.getString("commands.mute.time_help", "§7Время: 1d, 2h, 30m, 60s или 0/perm для перманентного");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить помощь по тихому муту
|
||||
*/
|
||||
public String getMuteSilentHelp() {
|
||||
return config.getString("commands.mute.silent_help", "§7-s - тихий мут (требует право lochat.mute.silent)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав на тихий мут
|
||||
*/
|
||||
public String getNoSilentPermission() {
|
||||
return config.getString("commands.mute.no_silent_permission", "§cУ вас нет права на тихий мут!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить причину мута по умолчанию
|
||||
*/
|
||||
public String getDefaultMuteReason() {
|
||||
return config.getString("commands.mute.default_reason", "Нету.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить длительность мута по умолчанию
|
||||
*/
|
||||
public String getDefaultMuteDuration() {
|
||||
return config.getString("commands.mute.default_duration", "7d");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение кулдауна
|
||||
*/
|
||||
public String getCooldownMessage() {
|
||||
return config.getString("commands.cooldown.message", "§cПодождите {remaining} сек.");
|
||||
}
|
||||
|
||||
// ========== ЛОКАЛЬНЫЙ ЧАТ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение "никто не услышал"
|
||||
*/
|
||||
public String getNobodyHeard() {
|
||||
return config.getString("local_chat.nobody_heard", "&#FFA726Вас никто не услышал - рядом нет игроков");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение "рядом нет игроков"
|
||||
*/
|
||||
public String getNoPlayersNearby() {
|
||||
return config.getString("local_chat.no_players_nearby", "&#FFA726Рядом нет игроков");
|
||||
}
|
||||
|
||||
// ========== ФИЛЬТРЫ ==========
|
||||
|
||||
/**
|
||||
* Получить минимальную длину для проверки капса
|
||||
*/
|
||||
public int getCapsMinLength() {
|
||||
return config.getInt("filters.caps.min_length", 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о блокировке капса
|
||||
*/
|
||||
public String getCapsBlockedMessage() {
|
||||
return config.getString("filters.caps.blocked_message", "§cСлишком много заглавных букв!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление об автоисправлении
|
||||
*/
|
||||
public String getAutoLowercaseNotice() {
|
||||
return config.getString("filters.caps.auto_lowercase_notice", "§7Ваше сообщение было автоматически исправлено");
|
||||
}
|
||||
|
||||
// ========== МУТИРОВАНИЕ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение о том что игрок замучен
|
||||
*/
|
||||
public String getMutedMessage() {
|
||||
return config.getString("mute.muted_message", "§cВы замучены! Причина: {reason}. Осталось: {time}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о перманентном муте
|
||||
*/
|
||||
public String getMutedPermanent() {
|
||||
return config.getString("mute.muted_permanent", "§cВы замучены навсегда! Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о муте игрока
|
||||
*/
|
||||
public String getPlayerMuted() {
|
||||
return config.getString("mute.player_muted", "§aИгрок {player} замучен на {time}. Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о перманентном муте игрока
|
||||
*/
|
||||
public String getPlayerMutedPermanent() {
|
||||
return config.getString("mute.player_muted_permanent", "§aИгрок {player} замучен навсегда. Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о тихом муте
|
||||
*/
|
||||
public String getPlayerMutedSilent() {
|
||||
return config.getString("mute.player_muted_silent", "§7[ТИХИЙ МУТ] {player} замучен на {time}");
|
||||
}
|
||||
|
||||
// ========== ИГРОВЫЕ РЕЖИМЫ ==========
|
||||
|
||||
/**
|
||||
* Получить название игрового режима
|
||||
*/
|
||||
public String getGamemodeName(String gamemode) {
|
||||
return config.getString("gamemodes." + gamemode.toLowerCase(), gamemode);
|
||||
}
|
||||
|
||||
// ========== ОБЩИЕ СООБЩЕНИЯ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав
|
||||
*/
|
||||
public String getNoPermission() {
|
||||
return config.getString("general.no_permission", "§cУ вас нет прав для выполнения этой команды!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение "только для игроков"
|
||||
*/
|
||||
public String getPlayerOnly() {
|
||||
return config.getString("general.player_only", "§cЭта команда доступна только игрокам!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о неверных аргументах
|
||||
*/
|
||||
public String getInvalidArguments() {
|
||||
return config.getString("general.invalid_arguments", "§cНеверные аргументы команды!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отключенной команде
|
||||
*/
|
||||
public String getCommandDisabled() {
|
||||
return config.getString("general.command_disabled", "§cЭта команда отключена!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отключенной функции
|
||||
*/
|
||||
public String getFeatureDisabled() {
|
||||
return config.getString("general.feature_disabled", "§cЭта функция отключена в конфигурации!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить формат времени
|
||||
*/
|
||||
public String getTimeFormat(String unit) {
|
||||
return config.getString("general.time_formats." + unit, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об игроке не найден
|
||||
*/
|
||||
public String getPlayerNotFound() {
|
||||
return config.getString("general.player_not_found", "§cИгрок не найден!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение игнорирования
|
||||
*/
|
||||
public String getIgnore(String key) {
|
||||
return config.getString("ignore." + key, "§cОшибка игнорирования!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение личных сообщений
|
||||
*/
|
||||
public String getPrivateMessage(String key) {
|
||||
return config.getString("private_messages." + key, "§cОшибка личных сообщений!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав на тихий размут
|
||||
*/
|
||||
public String getNoSilentUnmutePermission() {
|
||||
return config.getString("mute.no_silent_unmute_permission", "&#CF6679Нет права на тихий размут!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о том что игрок не замучен
|
||||
*/
|
||||
public String getPlayerNotMutedSimple() {
|
||||
return config.getString("mute.player_not_muted_simple", "&#CF6679Игрок не замучен!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,252 +1,452 @@
|
|||
package com.loki.lochat.core.filter;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonArray;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Главный фильтр сообщений.
|
||||
* Порядок: mute (в MessageService) → caps → swear → advertising → ip → repeat → flood → spam
|
||||
*/
|
||||
public class AdvancedMessageFilter {
|
||||
|
||||
// ── URL / IP / повторы ─────────────────────────────────────────────────────
|
||||
private static final Pattern URL_PATTERN = Pattern.compile(
|
||||
"(https?://)?([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
"(https?://)?([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern IP_PATTERN = Pattern.compile(
|
||||
"\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b"
|
||||
);
|
||||
|
||||
private static final Pattern REPEAT_PATTERN = Pattern.compile(
|
||||
"(.)\\1{3,}"
|
||||
);
|
||||
|
||||
// Паттерн для скрытых ссылок (h t t p, w w w и т.д.)
|
||||
"\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b");
|
||||
private static final Pattern REPEAT_PATTERN = Pattern.compile("(.)\\1{3,}");
|
||||
private static final Pattern HIDDEN_URL_PATTERN = Pattern.compile(
|
||||
"(?i)(h\\s*t\\s*t\\s*p|w\\s*w\\s*w|d\\s*o\\s*t\\s*c\\s*o\\s*m)",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
// Паттерн для доменов без http
|
||||
"(?i)(h\\s*t\\s*t\\s*p|w\\s*w\\s*w|d\\s*o\\s*t\\s*c\\s*o\\s*m)");
|
||||
private static final Pattern DOMAIN_PATTERN = Pattern.compile(
|
||||
"\\b([a-zA-Z0-9-]+\\.(com|net|org|ru|gg|me|io|xyz|tk|ml|ga|cf|gq))\\b",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final FileConfiguration config;
|
||||
private final List<String> whitelistedDomains;
|
||||
private final List<String> blacklistedDomains;
|
||||
private final CapsFilter capsFilter;
|
||||
|
||||
// ── Word filter ────────────────────────────────────────────────────────────
|
||||
private final Set<String> badWords = new HashSet<>();
|
||||
private final Set<String> badFragments = new HashSet<>();
|
||||
private final char replacementChar;
|
||||
private final boolean checkFragments;
|
||||
private final boolean ignoreCase;
|
||||
|
||||
// ── Anti-flood: сколько сообщений за период ────────────────────────────────
|
||||
// UUID → список timestamp'ов
|
||||
private final Map<UUID, Deque<Long>> floodTracker = new ConcurrentHashMap<>();
|
||||
|
||||
// ── Anti-spam: последние N сообщений игрока ────────────────────────────────
|
||||
private final Map<UUID, Deque<String>> spamTracker = new ConcurrentHashMap<>();
|
||||
|
||||
public AdvancedMessageFilter(FileConfiguration config) {
|
||||
this(config, null);
|
||||
}
|
||||
|
||||
public AdvancedMessageFilter(FileConfiguration config, JavaPlugin plugin) {
|
||||
this.config = config;
|
||||
this.whitelistedDomains = config.getStringList("filters.advertising.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.advertising.blacklist");
|
||||
|
||||
// Инициализация капс фильтра
|
||||
|
||||
int maxCapsPercent = config.getInt("filters.caps.max-percent", 70);
|
||||
int minLength = config.getInt("filters.caps.min-length", 5);
|
||||
boolean autoLowercase = config.getBoolean("filters.caps.auto-lowercase", true);
|
||||
boolean blockMessage = config.getBoolean("filters.caps.block", false);
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLowercase, blockMessage);
|
||||
int minLength = config.getInt("filters.caps.min-length", 5);
|
||||
boolean autoLower = config.getBoolean("filters.caps.auto-lowercase", true);
|
||||
boolean blockCaps = config.getBoolean("filters.caps.block", false);
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLower, blockCaps);
|
||||
|
||||
this.replacementChar = config.getString("filters.swear.replacement-char", "*").charAt(0);
|
||||
this.checkFragments = config.getBoolean("filters.swear.check-fragments", true);
|
||||
this.ignoreCase = config.getBoolean("filters.swear.ignore-case", true);
|
||||
|
||||
loadWordFilter(plugin);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Загрузка словаря
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void loadWordFilter(JavaPlugin plugin) {
|
||||
if (!config.getBoolean("filters.swear.enabled", true)) return;
|
||||
|
||||
// Встроенные слова из config
|
||||
List<String> builtIn = config.getStringList("filters.swear.words");
|
||||
for (String w : builtIn) {
|
||||
if (!w.isBlank() && !w.startsWith("плохое")) {
|
||||
badWords.add(ignoreCase ? w.toLowerCase() : w);
|
||||
}
|
||||
}
|
||||
|
||||
// Внешний файл filter-words.json
|
||||
if (!config.getBoolean("filters.swear.use-external-file", true)) return;
|
||||
if (plugin == null) return;
|
||||
|
||||
File jsonFile = new File(plugin.getDataFolder(), "filter-words.json");
|
||||
if (!jsonFile.exists()) {
|
||||
// Копируем из ресурсов
|
||||
plugin.saveResource("data/filter-words.json", false);
|
||||
jsonFile = new File(plugin.getDataFolder(), "data/filter-words.json");
|
||||
}
|
||||
if (!jsonFile.exists()) return;
|
||||
|
||||
try (FileReader reader = new FileReader(jsonFile)) {
|
||||
JsonObject root = new Gson().fromJson(reader, JsonObject.class);
|
||||
|
||||
JsonArray words = root.getAsJsonArray("words");
|
||||
if (words != null) {
|
||||
for (var el : words) {
|
||||
String w = el.getAsString();
|
||||
badWords.add(ignoreCase ? w.toLowerCase() : w);
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray fragments = root.getAsJsonArray("fragments");
|
||||
if (fragments != null && checkFragments) {
|
||||
for (var el : fragments) {
|
||||
String f = el.getAsString();
|
||||
badFragments.add(ignoreCase ? f.toLowerCase() : f);
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("[LoChat] Word filter loaded: "
|
||||
+ badWords.size() + " words, " + badFragments.size() + " fragments");
|
||||
} catch (Exception e) {
|
||||
if (plugin != null)
|
||||
plugin.getLogger().warning("[LoChat] Failed to load filter-words.json: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Главный метод
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public FilterResult filterMessage(Player player, String message) {
|
||||
// Капс фильтр
|
||||
|
||||
// 1. Капс
|
||||
if (config.getBoolean("filters.caps.enabled", true)) {
|
||||
String filtered = capsFilter.filter(player, message);
|
||||
if (filtered == null) {
|
||||
return new FilterResult(false, message, "&#FF6B6BНе кричите в чате!");
|
||||
return blocked("&#CF6679Не кричите в чате");
|
||||
}
|
||||
message = filtered;
|
||||
}
|
||||
|
||||
// Анти-реклама (объединенная проверка)
|
||||
// 2. Мат
|
||||
if (config.getBoolean("filters.swear.enabled", true)
|
||||
&& !player.hasPermission("lochat.bypass.swear")
|
||||
&& !player.hasPermission("lochat.bypass.filter")) {
|
||||
FilterResult swear = filterSwear(player, message);
|
||||
if (!swear.allowed()) return swear;
|
||||
message = swear.filteredMessage();
|
||||
}
|
||||
|
||||
// 3. Реклама / URL
|
||||
if (config.getBoolean("filters.advertising.enabled", true)) {
|
||||
// URL фильтр
|
||||
FilterResult urlResult = filterUrls(player, message);
|
||||
if (!urlResult.allowed()) {
|
||||
return urlResult;
|
||||
}
|
||||
message = urlResult.filteredMessage();
|
||||
FilterResult r = filterUrls(player, message);
|
||||
if (!r.allowed()) return r;
|
||||
message = r.filteredMessage();
|
||||
|
||||
// Скрытые ссылки
|
||||
FilterResult hiddenResult = filterHiddenUrls(player, message);
|
||||
if (!hiddenResult.allowed()) {
|
||||
return hiddenResult;
|
||||
}
|
||||
r = filterHiddenUrls(player, message);
|
||||
if (!r.allowed()) return r;
|
||||
|
||||
// Домены без http
|
||||
FilterResult domainResult = filterDomains(player, message);
|
||||
if (!domainResult.allowed()) {
|
||||
return domainResult;
|
||||
}
|
||||
r = filterDomains(player, message);
|
||||
if (!r.allowed()) return r;
|
||||
}
|
||||
|
||||
// IP фильтр
|
||||
// 4. IP
|
||||
if (config.getBoolean("filters.ip.enabled", true)) {
|
||||
FilterResult ipResult = filterIPs(player, message);
|
||||
if (!ipResult.allowed()) {
|
||||
return ipResult;
|
||||
}
|
||||
message = ipResult.filteredMessage();
|
||||
FilterResult r = filterIPs(player, message);
|
||||
if (!r.allowed()) return r;
|
||||
message = r.filteredMessage();
|
||||
}
|
||||
|
||||
// Повторяющиеся символы
|
||||
// 5. Повторяющиеся символы
|
||||
if (config.getBoolean("filters.repeat.enabled", true)) {
|
||||
message = filterRepeatingChars(message);
|
||||
}
|
||||
|
||||
// 6. Anti-flood (количество сообщений в период)
|
||||
if (config.getBoolean("filters.flood.enabled", true)
|
||||
&& !player.hasPermission("lochat.bypass.flood")
|
||||
&& !player.hasPermission("lochat.bypass.filter")) {
|
||||
FilterResult r = filterFlood(player);
|
||||
if (!r.allowed()) return r;
|
||||
}
|
||||
|
||||
// 7. Anti-spam (похожие сообщения)
|
||||
if (config.getBoolean("filters.spam.enabled", true)
|
||||
&& !player.hasPermission("lochat.bypass.spam")
|
||||
&& !player.hasPermission("lochat.bypass.filter")) {
|
||||
FilterResult r = filterSpam(player, message);
|
||||
if (!r.allowed()) return r;
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Фильтр мата
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private FilterResult filterSwear(Player player, String message) {
|
||||
String action = config.getString("filters.swear.action", "replace");
|
||||
String check = ignoreCase ? message.toLowerCase() : message;
|
||||
|
||||
// Проверка полных слов
|
||||
String[] tokens = check.split("\\s+");
|
||||
boolean hasBad = false;
|
||||
for (String token : tokens) {
|
||||
// убираем пунктуацию по краям
|
||||
String clean = token.replaceAll("^[^а-яёa-z]+|[^а-яёa-z]+$", "");
|
||||
if (badWords.contains(clean)) {
|
||||
hasBad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка фрагментов (подстрок)
|
||||
if (!hasBad && checkFragments) {
|
||||
for (String frag : badFragments) {
|
||||
if (check.contains(frag)) {
|
||||
hasBad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasBad) return new FilterResult(true, message, null);
|
||||
|
||||
return switch (action) {
|
||||
case "block" -> blocked(config.getString("filters.swear.block-message",
|
||||
"&#CF6679Мат запрещён"));
|
||||
case "replace" -> {
|
||||
String replaced = replaceSwear(message);
|
||||
yield new FilterResult(true, replaced, null);
|
||||
}
|
||||
default -> new FilterResult(true, message, null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет матные слова звёздочками, сохраняя длину слова.
|
||||
*/
|
||||
private String replaceSwear(String message) {
|
||||
String[] words = message.split("(?<=\\s)|(?=\\s)"); // разбиваем с сохранением пробелов
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String word : words) {
|
||||
String check = ignoreCase ? word.toLowerCase() : word;
|
||||
String cleanCheck = check.replaceAll("^[^а-яёa-z]+|[^а-яёa-z]+$", "");
|
||||
|
||||
boolean isBad = badWords.contains(cleanCheck);
|
||||
if (!isBad && checkFragments) {
|
||||
for (String frag : badFragments) {
|
||||
if (check.contains(frag)) { isBad = true; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (isBad) {
|
||||
result.append(String.valueOf(replacementChar).repeat(word.length()));
|
||||
} else {
|
||||
result.append(word);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Anti-flood
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private FilterResult filterFlood(Player player) {
|
||||
int maxMessages = config.getInt("filters.flood.max-messages", 5);
|
||||
int timePeriod = config.getInt("filters.flood.time-period", 10); // секунды
|
||||
long now = System.currentTimeMillis();
|
||||
long windowMs = timePeriod * 1000L;
|
||||
|
||||
Deque<Long> timestamps = floodTracker.computeIfAbsent(
|
||||
player.getUniqueId(), k -> new ArrayDeque<>());
|
||||
|
||||
// Удаляем старые timestamp'ы за пределами окна
|
||||
while (!timestamps.isEmpty() && now - timestamps.peekFirst() > windowMs) {
|
||||
timestamps.pollFirst();
|
||||
}
|
||||
|
||||
if (timestamps.size() >= maxMessages) {
|
||||
return blocked(config.getString("filters.flood.block-message",
|
||||
"&#CF6679Не флудите!"));
|
||||
}
|
||||
|
||||
timestamps.addLast(now);
|
||||
return new FilterResult(true, null, null);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Anti-spam (похожие сообщения)
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private FilterResult filterSpam(Player player, String message) {
|
||||
int maxSimilar = config.getInt("filters.spam.max-similar-messages", 3);
|
||||
int threshold = config.getInt("filters.spam.similarity-threshold", 80);
|
||||
|
||||
Deque<String> history = spamTracker.computeIfAbsent(
|
||||
player.getUniqueId(), k -> new ArrayDeque<>());
|
||||
|
||||
// Считаем сколько прошлых сообщений похожи
|
||||
long similarCount = history.stream()
|
||||
.filter(prev -> similarity(prev, message) >= threshold)
|
||||
.count();
|
||||
|
||||
if (similarCount >= maxSimilar) {
|
||||
return blocked(config.getString("filters.spam.block-message",
|
||||
"&#CF6679Не отправляйте одинаковые сообщения"));
|
||||
}
|
||||
|
||||
// Добавляем в историю (храним последние 10)
|
||||
history.addLast(message);
|
||||
if (history.size() > 10) history.pollFirst();
|
||||
|
||||
return new FilterResult(true, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Простая метрика схожести: процент общих символов (0-100).
|
||||
*/
|
||||
private int similarity(String a, String b) {
|
||||
if (a.equals(b)) return 100;
|
||||
int maxLen = Math.max(a.length(), b.length());
|
||||
if (maxLen == 0) return 100;
|
||||
int dist = levenshtein(
|
||||
a.toLowerCase().substring(0, Math.min(a.length(), 50)),
|
||||
b.toLowerCase().substring(0, Math.min(b.length(), 50)));
|
||||
return 100 - (dist * 100 / maxLen);
|
||||
}
|
||||
|
||||
private int levenshtein(String a, String b) {
|
||||
int[] dp = new int[b.length() + 1];
|
||||
for (int j = 0; j <= b.length(); j++) dp[j] = j;
|
||||
for (int i = 1; i <= a.length(); i++) {
|
||||
int prev = dp[0];
|
||||
dp[0] = i;
|
||||
for (int j = 1; j <= b.length(); j++) {
|
||||
int temp = dp[j];
|
||||
dp[j] = a.charAt(i - 1) == b.charAt(j - 1)
|
||||
? prev
|
||||
: 1 + Math.min(prev, Math.min(dp[j], dp[j - 1]));
|
||||
prev = temp;
|
||||
}
|
||||
}
|
||||
return dp[b.length()];
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// URL / IP / повторы — прежняя логика
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private FilterResult filterUrls(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.urlfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.urlfilter")) return ok(message);
|
||||
Matcher matcher = URL_PATTERN.matcher(message);
|
||||
|
||||
while (matcher.find()) {
|
||||
String url = matcher.group();
|
||||
String domain = extractDomain(url);
|
||||
|
||||
String domain = extractDomain(matcher.group());
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean isWhitelisted = whitelistedDomains.stream()
|
||||
boolean ok = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (!isWhitelisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BСсылки запрещены!"));
|
||||
}
|
||||
if (!ok) return blocked(config.getString("filters.url.blocked-message",
|
||||
"&#CF6679Ссылки запрещены!"));
|
||||
}
|
||||
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean isBlacklisted = blacklistedDomains.stream()
|
||||
boolean bad = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (isBlacklisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BЭта ссылка запрещена!"));
|
||||
}
|
||||
if (bad) return blocked(config.getString("filters.url.blocked-message",
|
||||
"&#CF6679Эта ссылка запрещена!"));
|
||||
}
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
return ok(message);
|
||||
}
|
||||
|
||||
private FilterResult filterIPs(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.ipfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.ipfilter")) return ok(message);
|
||||
Matcher matcher = IP_PATTERN.matcher(message);
|
||||
|
||||
if (matcher.find()) {
|
||||
String replacement = config.getString("filters.ip.replacement", "[IP скрыт]");
|
||||
String filtered = matcher.replaceAll(replacement);
|
||||
|
||||
if (config.getBoolean("filters.ip.block", false)) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.ip.blocked-message", "&#FF6B6BIP адреса запрещены!"));
|
||||
}
|
||||
|
||||
return new FilterResult(true, filtered, null);
|
||||
if (config.getBoolean("filters.ip.block", false))
|
||||
return blocked(config.getString("filters.ip.blocked-message",
|
||||
"&#CF6679IP адреса запрещены!"));
|
||||
return ok(matcher.replaceAll(config.getString("filters.ip.replacement", "[IP скрыт]")));
|
||||
}
|
||||
return ok(message);
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
private FilterResult filterHiddenUrls(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-hidden-urls", true)) return ok(message);
|
||||
if (player.hasPermission("lochat.bypass.hiddenurls")) return ok(message);
|
||||
if (HIDDEN_URL_PATTERN.matcher(message).find())
|
||||
return blocked(config.getString("filters.advertising.blocked-message",
|
||||
"&#CF6679Реклама запрещена!"));
|
||||
return ok(message);
|
||||
}
|
||||
|
||||
private FilterResult filterDomains(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-domains", true)) return ok(message);
|
||||
if (player.hasPermission("lochat.bypass.domains")) return ok(message);
|
||||
Matcher matcher = DOMAIN_PATTERN.matcher(message);
|
||||
while (matcher.find()) {
|
||||
String domain = matcher.group();
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean ok = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
if (!ok) return blocked(config.getString("filters.advertising.blocked-message",
|
||||
"&#CF6679Реклама запрещена!"));
|
||||
}
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean bad = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
if (bad) return blocked(config.getString("filters.advertising.blocked-message",
|
||||
"&#CF6679Реклама запрещена!"));
|
||||
}
|
||||
}
|
||||
return ok(message);
|
||||
}
|
||||
|
||||
private String filterRepeatingChars(String message) {
|
||||
int maxRepeats = config.getInt("filters.repeat.max-repeats", 3);
|
||||
|
||||
Matcher matcher = REPEAT_PATTERN.matcher(message);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String repeated = matcher.group(1);
|
||||
String replacement = repeated.repeat(Math.min(maxRepeats, matcher.group().length()));
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
int max = config.getInt("filters.repeat.max-repeats", 3);
|
||||
Matcher m = REPEAT_PATTERN.matcher(message);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
m.appendReplacement(sb, Matcher.quoteReplacement(
|
||||
m.group(1).repeat(Math.min(max, m.group().length()))));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
m.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String extractDomain(String url) {
|
||||
url = url.replaceFirst("^https?://", "");
|
||||
int slashIndex = url.indexOf('/');
|
||||
if (slashIndex != -1) {
|
||||
url = url.substring(0, slashIndex);
|
||||
}
|
||||
return url;
|
||||
int slash = url.indexOf('/');
|
||||
return slash != -1 ? url.substring(0, slash) : url;
|
||||
}
|
||||
|
||||
// ── Утилиты ────────────────────────────────────────────────────────────────
|
||||
|
||||
private static FilterResult ok(String msg) { return new FilterResult(true, msg, null); }
|
||||
private static FilterResult blocked(String reason) { return new FilterResult(false, null, reason); }
|
||||
|
||||
/**
|
||||
* Фильтрует скрытые ссылки (h t t p, w w w и т.д.)
|
||||
* Очищает трекеры для игрока при выходе (вызывать из PlayerEventListener).
|
||||
*/
|
||||
private FilterResult filterHiddenUrls(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-hidden-urls", true)) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.hiddenurls")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
Matcher matcher = HIDDEN_URL_PATTERN.matcher(message);
|
||||
if (matcher.find()) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
public void clearPlayer(java.util.UUID uuid) {
|
||||
floodTracker.remove(uuid);
|
||||
spamTracker.remove(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Фильтрует домены без http (example.com, discord.gg и т.д.)
|
||||
*/
|
||||
private FilterResult filterDomains(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-domains", true)) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.domains")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
Matcher matcher = DOMAIN_PATTERN.matcher(message);
|
||||
|
||||
while (matcher.find()) {
|
||||
String domain = matcher.group();
|
||||
|
||||
// Проверяем whitelist
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean isWhitelisted = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (!isWhitelisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем blacklist
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean isBlacklisted = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (isBlacklisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
public record FilterResult(boolean allowed, String filteredMessage, String blockReason) {
|
||||
}
|
||||
public record FilterResult(boolean allowed, String filteredMessage, String blockReason) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
public class CooldownFilter implements MessageFilter {
|
||||
private final CooldownService cooldownService;
|
||||
private final FileConfiguration config;
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public CooldownFilter(CooldownService cooldownService, JavaPlugin plugin) {
|
||||
this.cooldownService = cooldownService;
|
||||
this.config = plugin.getConfig();
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -32,7 +34,13 @@ public class CooldownFilter implements MessageFilter {
|
|||
if (cooldownService.isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(
|
||||
player.getUniqueId(), chatType, cooldown);
|
||||
player.sendMessage("§cПодождите " + remaining + " сек.");
|
||||
|
||||
// Получаем сообщение из конфигурации
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
String cooldownMessage = loChat.getConfigManager().getHardcodedMessages().getCooldownMessage();
|
||||
cooldownMessage = cooldownMessage.replace("{remaining}", String.valueOf(remaining));
|
||||
|
||||
player.sendMessage(cooldownMessage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,24 @@ package com.loki.lochat.core.filter;
|
|||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.config.HardcodedMessages;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Фильтр проверки мута
|
||||
*/
|
||||
public class MuteFilter implements MessageFilter {
|
||||
private final MuteService muteService;
|
||||
private final HardcodedMessages hardcodedMessages;
|
||||
|
||||
public MuteFilter(MuteService muteService) {
|
||||
public MuteFilter(MuteService muteService, JavaPlugin plugin) {
|
||||
this.muteService = muteService;
|
||||
// Получаем HardcodedMessages через LoChat
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
this.hardcodedMessages = loChat.getConfigManager().getHardcodedMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -26,7 +33,21 @@ public class MuteFilter implements MessageFilter {
|
|||
|
||||
private void sendMuteNotification(Player player) {
|
||||
long remaining = muteService.getRemainingTime(player.getUniqueId());
|
||||
String timeStr = muteService.formatTime(remaining);
|
||||
player.sendMessage("§cВы замучены! Осталось: " + timeStr);
|
||||
com.loki.lochat.data.model.MuteData muteData = muteService.getMuteData(player.getUniqueId());
|
||||
String reason = muteData != null && muteData.getReason() != null ? muteData.getReason() : "";
|
||||
|
||||
String msg;
|
||||
if (remaining < 0) {
|
||||
// Permanent mute
|
||||
msg = hardcodedMessages.getMutedPermanent()
|
||||
.replace("{reason}", reason);
|
||||
} else {
|
||||
String timeStr = muteService.formatTime(remaining);
|
||||
msg = hardcodedMessages.getMutedMessage()
|
||||
.replace("{reason}", reason)
|
||||
.replace("{time}", timeStr);
|
||||
}
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,15 @@ public class ServiceRegistry {
|
|||
private void registerServicesWithDeps(ConfigManager configManager, MessageConfig messageConfig) {
|
||||
register(ChatService.class, new ChatServiceImpl(plugin, this));
|
||||
register(MuteService.class, new MuteServiceImpl(plugin));
|
||||
register(CooldownService.class, new CooldownServiceImpl());
|
||||
|
||||
// CooldownService регистрируем раньше PlayerDataService, чтобы передать его как зависимость
|
||||
CooldownServiceImpl cooldownService = new CooldownServiceImpl();
|
||||
register(CooldownService.class, cooldownService);
|
||||
|
||||
register(MessageService.class, new MessageServiceImpl(plugin, this));
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin));
|
||||
|
||||
// Передаём тот же экземпляр CooldownService, а не новый — иначе очистка при выходе не работает
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin, cooldownService));
|
||||
|
||||
register(PMService.class, new PMServiceImpl());
|
||||
register(IgnoreService.class, new IgnoreServiceImpl(plugin));
|
||||
|
|
|
|||
|
|
@ -4,40 +4,47 @@ import com.loki.lochat.api.service.ChatService;
|
|||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.renderer.EnhancedChatRenderer;
|
||||
import com.loki.lochat.util.DistanceUtil;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса чата
|
||||
* Реализация сервиса чата.
|
||||
* globalChatDisabled персистируется в data/players.yml.
|
||||
*/
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
|
||||
private final File playersFile;
|
||||
|
||||
public ChatServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.playersFile = new File(plugin.getDataFolder(), "data/players.yml");
|
||||
loadDisabled();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Chat
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public void sendGlobalMessage(Player sender, Object message) {
|
||||
Component messageComponent = (message instanceof Component)
|
||||
? (Component) message
|
||||
: ChatFormatter.parse(message.toString());
|
||||
|
||||
// Используем тот же рендерер что и в ChatEventListener для единообразия
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, messageComponent, true);
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!isGlobalChatDisabled(player.getUniqueId())) {
|
||||
Component renderedMessage = renderer.render(sender, sender.displayName(), messageComponent, player);
|
||||
player.sendMessage(renderedMessage);
|
||||
Component msg = toComponent(message);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, msg, true);
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (!isGlobalChatDisabled(p.getUniqueId())) {
|
||||
p.sendMessage(renderer.render(sender, sender.displayName(), msg, p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,42 +52,81 @@ public class ChatServiceImpl implements ChatService {
|
|||
@Override
|
||||
public void sendLocalMessage(Player sender, Object message) {
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
|
||||
Component messageComponent = (message instanceof Component)
|
||||
? (Component) message
|
||||
: ChatFormatter.parse(message.toString());
|
||||
Component msg = toComponent(message);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, msg, false);
|
||||
|
||||
// Используем тот же рендерер что и в ChatEventListener для единообразия
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, messageComponent, false);
|
||||
|
||||
int recipientCount = 0;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
Component renderedMessage = renderer.render(sender, sender.displayName(), messageComponent, player);
|
||||
player.sendMessage(renderedMessage);
|
||||
recipientCount++;
|
||||
int count = 0;
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, p, radius)) {
|
||||
p.sendMessage(renderer.render(sender, sender.displayName(), msg, p));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Если никто не услышал (только отправитель в радиусе)
|
||||
if (recipientCount <= 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfig().getString("messages.local.nobody-heard", "<#FFA726>Вас никто не услышал - рядом нет игроков")));
|
||||
if (count <= 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(
|
||||
plugin.getConfig().getString("messages.local.nobody-heard",
|
||||
"<color:#9878C9>Вас никто не услышал — рядом нет игроков</color>")));
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Toggle global chat — с персистенцией
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public boolean toggleGlobalChat(UUID player) {
|
||||
boolean nowEnabled;
|
||||
if (globalChatDisabled.contains(player)) {
|
||||
globalChatDisabled.remove(player);
|
||||
return true;
|
||||
nowEnabled = true;
|
||||
} else {
|
||||
globalChatDisabled.add(player);
|
||||
return false;
|
||||
nowEnabled = false;
|
||||
}
|
||||
FoliaUtil.runAsync(plugin, this::saveDisabled);
|
||||
return nowEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalChatDisabled(UUID player) {
|
||||
return globalChatDisabled.contains(player);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Персистенция
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private void loadDisabled() {
|
||||
if (!playersFile.exists()) return;
|
||||
YamlConfiguration cfg = YamlConfiguration.loadConfiguration(playersFile);
|
||||
for (String uuidStr : cfg.getStringList("global-chat-disabled")) {
|
||||
try {
|
||||
globalChatDisabled.add(UUID.fromString(uuidStr));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDisabled() {
|
||||
try {
|
||||
ensureDir();
|
||||
YamlConfiguration cfg = playersFile.exists()
|
||||
? YamlConfiguration.loadConfiguration(playersFile)
|
||||
: new YamlConfiguration();
|
||||
|
||||
cfg.set("global-chat-disabled",
|
||||
globalChatDisabled.stream().map(UUID::toString).toList());
|
||||
cfg.save(playersFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[LoChat] Failed to save players.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureDir() {
|
||||
File dir = playersFile.getParentFile();
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
}
|
||||
|
||||
private Component toComponent(Object message) {
|
||||
return message instanceof Component c ? c : ChatFormatter.parse(message.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,24 +29,21 @@ public class MessageServiceImpl implements MessageService {
|
|||
this.filters = new ArrayList<>();
|
||||
|
||||
// Регистрируем фильтры в порядке применения
|
||||
filters.add(new MuteFilter(registry.get(MuteService.class)));
|
||||
// MuteFilter теперь получает plugin для доступа к HardcodedMessages
|
||||
filters.add(new MuteFilter(registry.get(MuteService.class), plugin));
|
||||
filters.add(new CooldownFilter(registry.get(CooldownService.class), plugin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Player player, String rawMessage) {
|
||||
// Создаем объект сообщения
|
||||
ChatMessage message = ChatMessage.create(player, rawMessage);
|
||||
|
||||
// Применяем фильтры (мут, кулдаун)
|
||||
for (MessageFilter filter : filters) {
|
||||
if (!filter.apply(player, message)) {
|
||||
return false; // Сообщение заблокировано
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Возвращаем true - сообщение разрешено
|
||||
// Рендерер сам отправит его
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,22 +73,21 @@ public class MuteServiceImpl implements MuteService {
|
|||
|
||||
@Override
|
||||
public boolean isMuted(UUID uuid) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
if (uuid == null) return false;
|
||||
|
||||
if (isExpired(data)) {
|
||||
mutes.remove(uuid);
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return false;
|
||||
|
||||
if (!isExpired(data)) return true;
|
||||
|
||||
// Атомарно удаляем только если значение не изменилось между get() и remove().
|
||||
// ConcurrentHashMap.remove(key, value) удаляет запись лишь когда она точно та же.
|
||||
boolean removed = mutes.remove(uuid, data);
|
||||
if (removed) {
|
||||
saveAsync();
|
||||
plugin.getLogger().info("Mute expired for player " + data.getPlayerName());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -2,27 +2,118 @@ package com.loki.lochat.core.service;
|
|||
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.api.service.PlayerDataService;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Реализация сервиса данных игроков
|
||||
* Реализация сервиса данных игроков.
|
||||
* Хранит и сохраняет статистику сообщений в statistics.yml.
|
||||
*/
|
||||
public class PlayerDataServiceImpl implements PlayerDataService {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
public PlayerDataServiceImpl(JavaPlugin plugin) {
|
||||
this.cooldownService = new CooldownServiceImpl();
|
||||
// Счётчики сообщений per-player (сбрасываются при выходе, сохраняются в файл)
|
||||
private final Map<UUID, Long> playerMessages = new ConcurrentHashMap<>();
|
||||
// Глобальные счётчики по типу чата
|
||||
private final AtomicLong globalChatCount = new AtomicLong(0);
|
||||
private final AtomicLong localChatCount = new AtomicLong(0);
|
||||
private final AtomicLong pmCount = new AtomicLong(0);
|
||||
private final AtomicLong totalMessages = new AtomicLong(0);
|
||||
|
||||
private final File statsFile;
|
||||
private YamlConfiguration stats;
|
||||
|
||||
public PlayerDataServiceImpl(JavaPlugin plugin, CooldownService cooldownService) {
|
||||
this.plugin = plugin;
|
||||
this.cooldownService = cooldownService;
|
||||
this.statsFile = new File(plugin.getDataFolder(), "data/statistics.yml");
|
||||
load();
|
||||
}
|
||||
|
||||
// ── Публичные методы статистики ────────────────────────────────────────────
|
||||
|
||||
/** Вызывать при каждом сообщении в чате */
|
||||
public void recordMessage(UUID player, String chatType) {
|
||||
playerMessages.merge(player, 1L, Long::sum);
|
||||
totalMessages.incrementAndGet();
|
||||
switch (chatType) {
|
||||
case "global" -> globalChatCount.incrementAndGet();
|
||||
case "local" -> localChatCount.incrementAndGet();
|
||||
case "pm" -> pmCount.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
public long getPlayerMessages(UUID player) {
|
||||
return playerMessages.getOrDefault(player, 0L);
|
||||
}
|
||||
|
||||
// ── PlayerDataService ──────────────────────────────────────────────────────
|
||||
|
||||
@Override
|
||||
public void clearPlayerData(UUID player) {
|
||||
cooldownService.removeCooldown(player);
|
||||
// Сохраняем статистику игрока в файл при выходе
|
||||
long msgs = playerMessages.getOrDefault(player, 0L);
|
||||
if (msgs > 0) {
|
||||
FoliaUtil.runAsync(plugin, () -> persistPlayerMessages(player, msgs));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
// Сохранение всех данных при выключении
|
||||
try {
|
||||
ensureDir();
|
||||
// Обновляем глобальные счётчики
|
||||
stats.set("global.total-messages",
|
||||
stats.getLong("global.total-messages", 0) + totalMessages.getAndSet(0));
|
||||
stats.set("by-type.global-chat",
|
||||
stats.getLong("by-type.global-chat", 0) + globalChatCount.getAndSet(0));
|
||||
stats.set("by-type.local-chat",
|
||||
stats.getLong("by-type.local-chat", 0) + localChatCount.getAndSet(0));
|
||||
stats.set("by-type.private-messages",
|
||||
stats.getLong("by-type.private-messages", 0) + pmCount.getAndSet(0));
|
||||
stats.set("last-updated", System.currentTimeMillis());
|
||||
stats.save(statsFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[LoChat] Failed to save statistics: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ── Внутренние методы ──────────────────────────────────────────────────────
|
||||
|
||||
private void persistPlayerMessages(UUID uuid, long count) {
|
||||
try {
|
||||
String path = "top-players.by-messages." + uuid;
|
||||
long prev = stats.getLong(path + ".count", 0);
|
||||
stats.set(path + ".count", prev + count);
|
||||
stats.set(path + ".last-updated", System.currentTimeMillis());
|
||||
stats.save(statsFile);
|
||||
playerMessages.remove(uuid);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("[LoChat] Failed to persist player stats: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
ensureDir();
|
||||
if (!statsFile.exists()) {
|
||||
plugin.saveResource("data/statistics.yml", false);
|
||||
}
|
||||
stats = YamlConfiguration.loadConfiguration(statsFile);
|
||||
}
|
||||
|
||||
private void ensureDir() {
|
||||
File dir = statsFile.getParentFile();
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public class GradientModule {
|
|||
public GradientModule(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.enabled = false;
|
||||
this.luckPermsHook = null; // Явно инициализируем как null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,7 +156,7 @@ public class GradientModule {
|
|||
|
||||
// LuckPerms префикс
|
||||
String lpPrefix = null;
|
||||
if (luckPermsHook.isEnabled()) {
|
||||
if (luckPermsHook != null && luckPermsHook.isEnabled()) {
|
||||
lpPrefix = luckPermsHook.getActivePrefix(player);
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +242,7 @@ public class GradientModule {
|
|||
}
|
||||
|
||||
// LuckPerms префикс
|
||||
if (luckPermsHook.isEnabled()) {
|
||||
if (luckPermsHook != null && luckPermsHook.isEnabled()) {
|
||||
String lpPrefix = luckPermsHook.getActivePrefix(player);
|
||||
if (lpPrefix != null && !lpPrefix.isEmpty()) {
|
||||
if (data.hasColors() && data.isColorEnabled() && config.isGradientOnLuckPermsPrefix()) {
|
||||
|
|
@ -259,7 +260,7 @@ public class GradientModule {
|
|||
* Получает только LuckPerms префикс
|
||||
*/
|
||||
public String getLuckPermsPrefix(Player player) {
|
||||
if (!enabled || !luckPermsHook.isEnabled()) return "";
|
||||
if (!enabled || luckPermsHook == null || !luckPermsHook.isEnabled()) return "";
|
||||
String prefix = luckPermsHook.getActivePrefix(player);
|
||||
return prefix != null ? prefix : "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,18 +52,18 @@ public class DiscordIntegration {
|
|||
private void initWebhook() {
|
||||
if (!enabled) return;
|
||||
|
||||
// Получаем конфигурацию внешнего вида для дефолтных значений
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.config.AppearanceConfig appearanceConfig = loChat.getConfigManager().getAppearanceConfig();
|
||||
|
||||
String webhookUrl = config.getString("webhook.url", "");
|
||||
String username = config.getString("webhook.username", "Minecraft Server");
|
||||
String avatarUrl = config.getString("webhook.avatar_url", "https://mc-heads.net/avatar/minecraft/64");
|
||||
String avatarUrl = config.getString("webhook.avatar_url", appearanceConfig.getDefaultAvatarUrl());
|
||||
int timeout = config.getInt("performance.timeout", 10);
|
||||
int retryAttempts = config.getInt("performance.retry_attempts", 3);
|
||||
long retryDelay = config.getLong("performance.retry_delay", 1000);
|
||||
|
||||
// Логируем информацию о URL для отладки
|
||||
plugin.getLogger().info("Discord webhook URL length: " + webhookUrl.length());
|
||||
if (webhookUrl.length() > 0) {
|
||||
plugin.getLogger().info("Discord webhook URL starts with: " + webhookUrl.substring(0, Math.min(50, webhookUrl.length())));
|
||||
}
|
||||
// Логируем только факт наличия URL, но не его содержимое (security)
|
||||
|
||||
webhook = new DiscordWebhook(plugin, webhookUrl, username, avatarUrl, timeout, retryAttempts, retryDelay);
|
||||
|
||||
|
|
@ -118,9 +118,21 @@ public class DiscordIntegration {
|
|||
*/
|
||||
private void sendChatMessageInternal(String message, String playerName, boolean isGlobal) {
|
||||
if (config.getBoolean("chat.use_embed", true)) {
|
||||
String title = isGlobal ? "🌍 Глобальный чат" : "📍 Локальный чат";
|
||||
String color = config.getString("chat.embed_color", "5865F2");
|
||||
String thumbnail = "https://mc-heads.net/avatar/" + playerName + "/64";
|
||||
// Получаем заголовки из конфигурации внешнего вида
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.config.AppearanceConfig appearanceConfig = loChat.getConfigManager().getAppearanceConfig();
|
||||
|
||||
String title = isGlobal ?
|
||||
appearanceConfig.getDiscordEventTitle("global_chat") :
|
||||
appearanceConfig.getDiscordEventTitle("local_chat");
|
||||
|
||||
// Если заголовок не найден в новой конфигурации, используем старый способ
|
||||
if (title.isEmpty()) {
|
||||
title = isGlobal ? "🌍 Глобальный чат" : "📍 Локальный чат";
|
||||
}
|
||||
|
||||
String color = config.getString("chat.embed_color", appearanceConfig.getDefaultEmbedColor());
|
||||
String thumbnail = appearanceConfig.getPlayerAvatarUrl().replace("{player}", playerName);
|
||||
|
||||
webhook.sendEmbed(title, message, color, thumbnail);
|
||||
} else {
|
||||
|
|
@ -135,12 +147,16 @@ public class DiscordIntegration {
|
|||
if (!enabled || !config.getBoolean("events.enabled", true) ||
|
||||
!config.getBoolean("events.join.enabled", true)) return;
|
||||
|
||||
// Получаем конфигурацию внешнего вида
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.config.AppearanceConfig appearanceConfig = loChat.getConfigManager().getAppearanceConfig();
|
||||
|
||||
String playerName = player.getName();
|
||||
String title = config.getString("events.join.title", "🟢 Игрок зашел на сервер");
|
||||
String title = config.getString("events.join.title", appearanceConfig.getDiscordEventTitle("join"));
|
||||
String description = config.getString("events.join.description", "**{player}** присоединился к серверу")
|
||||
.replace("{player}", playerName);
|
||||
String color = config.getString("events.join.color", "00FF00");
|
||||
String thumbnail = config.getString("events.join.thumbnail", "https://mc-heads.net/avatar/{player}/64")
|
||||
String thumbnail = config.getString("events.join.thumbnail", appearanceConfig.getPlayerAvatarUrl())
|
||||
.replace("{player}", playerName);
|
||||
|
||||
if (config.getBoolean("performance.async", true)) {
|
||||
|
|
@ -157,12 +173,16 @@ public class DiscordIntegration {
|
|||
if (!enabled || !config.getBoolean("events.enabled", true) ||
|
||||
!config.getBoolean("events.quit.enabled", true)) return;
|
||||
|
||||
// Получаем конфигурацию внешнего вида
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.config.AppearanceConfig appearanceConfig = loChat.getConfigManager().getAppearanceConfig();
|
||||
|
||||
String playerName = player.getName();
|
||||
String title = config.getString("events.quit.title", "🔴 Игрок покинул сервер");
|
||||
String title = config.getString("events.quit.title", appearanceConfig.getDiscordEventTitle("quit"));
|
||||
String description = config.getString("events.quit.description", "**{player}** покинул сервер")
|
||||
.replace("{player}", playerName);
|
||||
String color = config.getString("events.quit.color", "FF0000");
|
||||
String thumbnail = config.getString("events.quit.thumbnail", "https://mc-heads.net/avatar/{player}/64")
|
||||
String thumbnail = config.getString("events.quit.thumbnail", appearanceConfig.getPlayerAvatarUrl())
|
||||
.replace("{player}", playerName);
|
||||
|
||||
if (config.getBoolean("performance.async", true)) {
|
||||
|
|
@ -179,13 +199,17 @@ public class DiscordIntegration {
|
|||
if (!enabled || !config.getBoolean("events.enabled", true) ||
|
||||
!config.getBoolean("events.death.enabled", true)) return;
|
||||
|
||||
// Получаем конфигурацию внешнего вида
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.config.AppearanceConfig appearanceConfig = loChat.getConfigManager().getAppearanceConfig();
|
||||
|
||||
String playerName = player.getName();
|
||||
String title = config.getString("events.death.title", "💀 Смерть игрока");
|
||||
String title = config.getString("events.death.title", appearanceConfig.getDiscordEventTitle("death"));
|
||||
String description = config.getString("events.death.description", "**{player}** {death_message}")
|
||||
.replace("{player}", playerName)
|
||||
.replace("{death_message}", sanitizeMessage(deathMessage));
|
||||
String color = config.getString("events.death.color", "800080");
|
||||
String thumbnail = config.getString("events.death.thumbnail", "https://mc-heads.net/avatar/{player}/64")
|
||||
String thumbnail = config.getString("events.death.thumbnail", appearanceConfig.getPlayerAvatarUrl())
|
||||
.replace("{player}", playerName);
|
||||
|
||||
if (config.getBoolean("performance.async", true)) {
|
||||
|
|
@ -241,6 +265,13 @@ public class DiscordIntegration {
|
|||
webhook.sendEmbed(title, description, color, thumbnail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановить executor при выключении плагина
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (webhook != null) webhook.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить включена ли интеграция
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.loki.lochat.integrations;
|
|||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -12,11 +11,16 @@ import java.net.URI;
|
|||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Класс для отправки сообщений в Discord через вебхуки
|
||||
* Отправка сообщений в Discord через вебхуки.
|
||||
* Все запросы идут в выделенный daemon-поток LoChat-Discord,
|
||||
* а не в ForkJoinPool.commonPool().
|
||||
*/
|
||||
public class DiscordWebhook {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final String webhookUrl;
|
||||
private final String username;
|
||||
|
|
@ -25,188 +29,127 @@ public class DiscordWebhook {
|
|||
private final int retryAttempts;
|
||||
private final long retryDelay;
|
||||
|
||||
public DiscordWebhook(JavaPlugin plugin, String webhookUrl, String username, String avatarUrl,
|
||||
int timeout, int retryAttempts, long retryDelay) {
|
||||
// Выделенный пул — не засоряем ForkJoinPool.commonPool()
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor(
|
||||
r -> {
|
||||
Thread t = new Thread(r, "LoChat-Discord");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
);
|
||||
|
||||
public DiscordWebhook(JavaPlugin plugin, String webhookUrl, String username, String avatarUrl,
|
||||
int timeout, int retryAttempts, long retryDelay) {
|
||||
this.plugin = plugin;
|
||||
this.webhookUrl = webhookUrl != null ? webhookUrl.trim() : null; // Убираем пробелы
|
||||
this.webhookUrl = webhookUrl != null ? webhookUrl.trim() : null;
|
||||
this.username = username;
|
||||
this.avatarUrl = avatarUrl;
|
||||
this.timeout = timeout * 1000; // конвертируем в миллисекунды
|
||||
this.timeout = timeout * 1000;
|
||||
this.retryAttempts = retryAttempts;
|
||||
this.retryDelay = retryDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправить простое текстовое сообщение
|
||||
*/
|
||||
// ── Публичные методы отправки ─────────────────────────────────────────────
|
||||
|
||||
public CompletableFuture<Boolean> sendMessage(String content) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("content", content);
|
||||
json.addProperty("username", username);
|
||||
json.addProperty("avatar_url", avatarUrl);
|
||||
|
||||
return sendWebhook(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправить embed сообщение
|
||||
*/
|
||||
public CompletableFuture<Boolean> sendEmbed(String title, String description, String color, String thumbnailUrl) {
|
||||
JsonObject embed = new JsonObject();
|
||||
if (title != null) embed.addProperty("title", title);
|
||||
if (description != null) embed.addProperty("description", description);
|
||||
if (color != null) embed.addProperty("color", Integer.parseInt(color, 16));
|
||||
|
||||
if (thumbnailUrl != null) {
|
||||
JsonObject thumbnail = new JsonObject();
|
||||
thumbnail.addProperty("url", thumbnailUrl);
|
||||
embed.add("thumbnail", thumbnail);
|
||||
}
|
||||
|
||||
// Добавляем timestamp
|
||||
embed.addProperty("timestamp", java.time.Instant.now().toString());
|
||||
|
||||
JsonArray embeds = new JsonArray();
|
||||
embeds.add(embed);
|
||||
|
||||
JsonObject json = new JsonObject();
|
||||
json.add("embeds", embeds);
|
||||
json.addProperty("username", username);
|
||||
json.addProperty("avatar_url", avatarUrl);
|
||||
|
||||
return sendWebhook(json);
|
||||
return sendWebhook(buildEmbed(title, description, color, thumbnailUrl, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправить embed с кастомными полями
|
||||
*/
|
||||
public CompletableFuture<Boolean> sendEmbedWithFields(String title, String description, String color,
|
||||
String thumbnailUrl, JsonArray fields) {
|
||||
public CompletableFuture<Boolean> sendEmbedWithFields(String title, String description, String color,
|
||||
String thumbnailUrl, JsonArray fields) {
|
||||
return sendWebhook(buildEmbed(title, description, color, thumbnailUrl, fields));
|
||||
}
|
||||
|
||||
/** Остановить executor при выключении плагина */
|
||||
public void shutdown() {
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
// ── Внутренние методы ─────────────────────────────────────────────────────
|
||||
|
||||
private JsonObject buildEmbed(String title, String description, String color,
|
||||
String thumbnailUrl, JsonArray fields) {
|
||||
JsonObject embed = new JsonObject();
|
||||
if (title != null) embed.addProperty("title", title);
|
||||
if (description != null) embed.addProperty("description", description);
|
||||
if (color != null) embed.addProperty("color", Integer.parseInt(color, 16));
|
||||
if (fields != null) embed.add("fields", fields);
|
||||
|
||||
if (thumbnailUrl != null) {
|
||||
JsonObject thumbnail = new JsonObject();
|
||||
thumbnail.addProperty("url", thumbnailUrl);
|
||||
embed.add("thumbnail", thumbnail);
|
||||
JsonObject thumb = new JsonObject();
|
||||
thumb.addProperty("url", thumbnailUrl);
|
||||
embed.add("thumbnail", thumb);
|
||||
}
|
||||
|
||||
embed.addProperty("timestamp", java.time.Instant.now().toString());
|
||||
|
||||
|
||||
JsonArray embeds = new JsonArray();
|
||||
embeds.add(embed);
|
||||
|
||||
|
||||
JsonObject json = new JsonObject();
|
||||
json.add("embeds", embeds);
|
||||
json.addProperty("username", username);
|
||||
json.addProperty("avatar_url", avatarUrl);
|
||||
|
||||
return sendWebhook(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправить JSON в вебхук с повторными попытками
|
||||
* Отправляет JSON в вебхук асинхронно через выделенный executor.
|
||||
*/
|
||||
private CompletableFuture<Boolean> sendWebhook(JsonObject json) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
for (int attempt = 1; attempt <= retryAttempts; attempt++) {
|
||||
try {
|
||||
boolean success = sendWebhookSync(json.toString());
|
||||
if (success) {
|
||||
return true;
|
||||
}
|
||||
if (sendWebhookSync(json.toString())) return true;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Discord webhook attempt " + attempt + " failed: " + e.getMessage());
|
||||
}
|
||||
|
||||
// Ждем перед следующей попыткой (кроме последней)
|
||||
if (attempt < retryAttempts) {
|
||||
try {
|
||||
Thread.sleep(retryDelay);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
try { Thread.sleep(retryDelay); }
|
||||
catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().severe("Failed to send Discord webhook after " + retryAttempts + " attempts");
|
||||
return false;
|
||||
});
|
||||
}, executor); // ← передаём свой executor, не commonPool
|
||||
}
|
||||
|
||||
/**
|
||||
* Синхронная отправка в вебхук
|
||||
*/
|
||||
private boolean sendWebhookSync(String jsonPayload) throws IOException {
|
||||
String cleanUrl = webhookUrl.trim();
|
||||
URL url = URI.create(cleanUrl).toURL();
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
URL url = URI.create(webhookUrl.trim()).toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
try {
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "LoChat-Discord-Integration/1.0");
|
||||
connection.setDoOutput(true);
|
||||
connection.setConnectTimeout(timeout);
|
||||
connection.setReadTimeout(timeout);
|
||||
|
||||
// Отправляем JSON
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
byte[] input = jsonPayload.getBytes(StandardCharsets.UTF_8);
|
||||
os.write(input, 0, input.length);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("User-Agent", "LoChat-Discord-Integration/1.0");
|
||||
conn.setDoOutput(true);
|
||||
conn.setConnectTimeout(timeout);
|
||||
conn.setReadTimeout(timeout);
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(jsonPayload.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
if (responseCode == 204) {
|
||||
return true; // Успешная отправка
|
||||
} else if (responseCode == 429) {
|
||||
plugin.getLogger().warning("Discord webhook rate limited (429)");
|
||||
return false;
|
||||
} else {
|
||||
plugin.getLogger().warning("Discord webhook returned code: " + responseCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
int code = conn.getResponseCode();
|
||||
if (code == 204) return true;
|
||||
if (code == 429) { plugin.getLogger().warning("Discord webhook rate limited (429)"); return false; }
|
||||
plugin.getLogger().warning("Discord webhook returned code: " + code);
|
||||
return false;
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить валидность вебхука
|
||||
*/
|
||||
public boolean isValid() {
|
||||
if (webhookUrl == null || webhookUrl.trim().isEmpty()) {
|
||||
plugin.getLogger().warning("Discord webhook URL is null or empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
String cleanUrl = webhookUrl.trim();
|
||||
|
||||
if (!cleanUrl.startsWith("https://discord.com/api/webhooks/") &&
|
||||
!cleanUrl.startsWith("https://discordapp.com/api/webhooks/")) {
|
||||
plugin.getLogger().warning("Discord webhook URL has invalid format: " + cleanUrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cleanUrl.length() < 50) {
|
||||
plugin.getLogger().warning("Discord webhook URL is too short: " + cleanUrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверяем на недопустимые символы
|
||||
try {
|
||||
URI.create(cleanUrl);
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Discord webhook URL contains illegal characters: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (webhookUrl == null || webhookUrl.trim().isEmpty()) return false;
|
||||
String url = webhookUrl.trim();
|
||||
if (!url.startsWith("https://discord.com/api/webhooks/") &&
|
||||
!url.startsWith("https://discordapp.com/api/webhooks/")) return false;
|
||||
if (url.length() < 50) return false;
|
||||
try { URI.create(url); } catch (IllegalArgumentException e) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import com.loki.lochat.api.service.MessageService;
|
|||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.renderer.EnhancedChatRenderer;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
|
@ -18,70 +20,88 @@ public class ChatEventListener implements Listener {
|
|||
private final MessageService messageService;
|
||||
private final AdvancedMessageFilter advancedFilter;
|
||||
|
||||
public ChatEventListener(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
public ChatEventListener(JavaPlugin plugin, ServiceRegistry registry, com.loki.lochat.core.filter.AdvancedMessageFilter filter) {
|
||||
this.plugin = plugin;
|
||||
this.messageService = registry.get(MessageService.class);
|
||||
this.advancedFilter = new AdvancedMessageFilter(plugin.getConfig());
|
||||
this.advancedFilter = filter;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onChat(AsyncChatEvent event) {
|
||||
Player sender = event.getPlayer();
|
||||
|
||||
// plain нужен только для фильтров — цвета игрока обрабатываем ниже отдельно
|
||||
String rawMessage = PlainTextComponentSerializer.plainText().serialize(event.message());
|
||||
|
||||
// Определяем тип чата и очищаем сообщение от !
|
||||
boolean isGlobal = rawMessage.startsWith("!");
|
||||
String message = isGlobal ? rawMessage.substring(1).stripLeading() : rawMessage;
|
||||
String plainMessage = isGlobal ? rawMessage.substring(1).stripLeading() : rawMessage;
|
||||
|
||||
boolean allowed = messageService.processMessage(sender, message);
|
||||
|
||||
if (!allowed) {
|
||||
// Мут, кулдаун
|
||||
if (!messageService.processMessage(sender, plainMessage)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
AdvancedMessageFilter.FilterResult filterResult = advancedFilter.filterMessage(sender, message);
|
||||
|
||||
// Капс, реклама, повторы
|
||||
AdvancedMessageFilter.FilterResult filterResult = advancedFilter.filterMessage(sender, plainMessage);
|
||||
if (!filterResult.allowed()) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(filterResult.blockReason()));
|
||||
sender.sendMessage(ChatFormatter.parse(filterResult.blockReason()));
|
||||
return;
|
||||
}
|
||||
|
||||
String filteredMessage = filterResult.filteredMessage();
|
||||
|
||||
// Убеждаемся что ! удален из отображаемого сообщения
|
||||
if (isGlobal && filteredMessage.startsWith("!")) {
|
||||
filteredMessage = filteredMessage.substring(1).stripLeading();
|
||||
}
|
||||
|
||||
event.message(com.loki.lochat.utils.ChatFormatter.parse(filteredMessage));
|
||||
|
||||
// Фильтруем получателей для локального чата
|
||||
// ── Фиксируем цвет текста ──────────────────────────────────────────────
|
||||
// Проблема: Paper наследует цвет от предыдущего компонента если явный не задан.
|
||||
// Синий цвет в LOCAL — это цвет последней буквы градиента "LOCAL" (#4169E1),
|
||||
// который просачивается в текст сообщения.
|
||||
// Решение: всегда явно задаём цвет сообщения.
|
||||
// • lochat.chat.colors → парсим &7 / &#RRGGBB / MiniMessage из самого текста
|
||||
// • без права → берём message-color из конфига
|
||||
LoChat loChat = (LoChat) plugin;
|
||||
Component messageComponent;
|
||||
|
||||
if (sender.hasPermission("lochat.chat.colors")) {
|
||||
messageComponent = ChatFormatter.parse(filteredMessage);
|
||||
} else {
|
||||
String defaultColor = isGlobal
|
||||
? loChat.getConfigManager().getAppearanceConfig().getGlobalMessageColor()
|
||||
: loChat.getConfigManager().getAppearanceConfig().getLocalMessageColor();
|
||||
String hex = defaultColor.startsWith("#") ? defaultColor : "#" + defaultColor;
|
||||
messageComponent = ChatFormatter.parse("<color:" + hex + ">" + filteredMessage + "</color>");
|
||||
}
|
||||
|
||||
event.message(messageComponent);
|
||||
|
||||
// Радиус для локального чата
|
||||
if (!isGlobal) {
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
event.viewers().removeIf(viewer ->
|
||||
viewer instanceof Player p && !com.loki.lochat.util.DistanceUtil.isInRange(sender, p, radius)
|
||||
int radius = loChat.getConfigManager().getAppearanceConfig().getLocalRadius();
|
||||
event.viewers().removeIf(v ->
|
||||
v instanceof Player p && !com.loki.lochat.util.DistanceUtil.isInRange(sender, p, radius)
|
||||
);
|
||||
|
||||
// Проверяем, есть ли получатели (кроме отправителя)
|
||||
long recipientCount = event.viewers().stream()
|
||||
.filter(viewer -> viewer instanceof Player p && !p.equals(sender))
|
||||
.count();
|
||||
|
||||
if (recipientCount == 0) {
|
||||
// Отправляем уведомление отправителю после обработки события (Folia-совместимо)
|
||||
sender.getScheduler().run(plugin, (task) ->
|
||||
sender.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(plugin.getConfig().getString("messages.local.nobody-heard", "<#FFA726>Вас никто не услышал - рядом нет игроков"))), null
|
||||
|
||||
long recipients = event.viewers().stream()
|
||||
.filter(v -> v instanceof Player p && !p.equals(sender))
|
||||
.count();
|
||||
|
||||
if (recipients == 0) {
|
||||
String msg = loChat.getConfigManager().getHardcodedMessages().getNobodyHeard();
|
||||
sender.getScheduler().run(plugin, t ->
|
||||
sender.sendMessage(ChatFormatter.parse(msg)), null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
event.renderer(new EnhancedChatRenderer(plugin, sender, event.message(), isGlobal));
|
||||
|
||||
// Отправляем сообщение в Discord
|
||||
if (plugin instanceof LoChat loChat) {
|
||||
loChat.getDiscordIntegration().sendChatMessage(sender, filteredMessage, isGlobal);
|
||||
event.renderer(new EnhancedChatRenderer(plugin, sender, messageComponent, isGlobal));
|
||||
|
||||
loChat.getDiscordIntegration().sendChatMessage(sender, filteredMessage, isGlobal);
|
||||
|
||||
// Статистика
|
||||
com.loki.lochat.api.service.PlayerDataService pds =
|
||||
loChat.getServiceRegistry().get(com.loki.lochat.api.service.PlayerDataService.class);
|
||||
if (pds instanceof com.loki.lochat.core.service.PlayerDataServiceImpl impl) {
|
||||
impl.recordMessage(sender.getUniqueId(), isGlobal ? "global" : "local");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.loki.lochat.listener;
|
|||
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.api.service.PlayerDataService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
|
@ -15,16 +18,21 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
|||
public class PlayerEventListener implements Listener {
|
||||
private final PlayerDataService playerDataService;
|
||||
private final NickService nickService;
|
||||
private final PMService pmService;
|
||||
private final SpyService spyService;
|
||||
private final AdvancedMessageFilter advancedFilter;
|
||||
|
||||
public PlayerEventListener(ServiceRegistry registry) {
|
||||
this.playerDataService = registry.get(PlayerDataService.class);
|
||||
this.nickService = registry.get(NickService.class);
|
||||
public PlayerEventListener(ServiceRegistry registry, AdvancedMessageFilter advancedFilter) {
|
||||
this.playerDataService = registry.get(PlayerDataService.class);
|
||||
this.nickService = registry.get(NickService.class);
|
||||
this.pmService = registry.get(PMService.class);
|
||||
this.spyService = registry.get(SpyService.class);
|
||||
this.advancedFilter = advancedFilter;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
// Обновляем displayName с кастомным ником если есть
|
||||
if (nickService != null) {
|
||||
nickService.updatePlayerDisplay(player);
|
||||
}
|
||||
|
|
@ -33,6 +41,13 @@ public class PlayerEventListener implements Listener {
|
|||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
playerDataService.clearPlayerData(player.getUniqueId());
|
||||
java.util.UUID uuid = player.getUniqueId();
|
||||
|
||||
playerDataService.clearPlayerData(uuid);
|
||||
pmService.removeConversation(uuid);
|
||||
spyService.removeSpy(uuid);
|
||||
|
||||
// Очищаем flood / spam трекеры — иначе данные висят в памяти вечно
|
||||
advancedFilter.clearPlayer(uuid);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,113 +8,173 @@ import net.kyori.adventure.text.Component;
|
|||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Рендерер чата с поддержкой глобального/локального режимов
|
||||
* Рендерер чата — глобальный и локальный режимы.
|
||||
* Цвета PREFIX и текста сообщения полностью настраиваются через chat-appearance.yml.
|
||||
*/
|
||||
public class EnhancedChatRenderer implements ChatRenderer {
|
||||
|
||||
private static final TextColor FALLBACK_COLOR = NamedTextColor.WHITE;
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final Player sender;
|
||||
private final Component originalMessage;
|
||||
private final boolean isGlobal;
|
||||
private final MentionHandler mentionHandler;
|
||||
private final com.loki.lochat.config.AppearanceConfig cfg;
|
||||
private final com.loki.lochat.config.HardcodedMessages hm;
|
||||
|
||||
public EnhancedChatRenderer(JavaPlugin plugin, Player sender, Component originalMessage, boolean isGlobal) {
|
||||
this.plugin = plugin;
|
||||
this.sender = sender;
|
||||
this.originalMessage = originalMessage;
|
||||
this.isGlobal = isGlobal;
|
||||
this.mentionHandler = new MentionHandler(plugin);
|
||||
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
this.cfg = loChat.getConfigManager().getAppearanceConfig();
|
||||
this.hm = loChat.getConfigManager().getHardcodedMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
|
||||
// Красивые префиксы с градиентами и эмодзи
|
||||
Component prefixComponent;
|
||||
if (isGlobal) {
|
||||
// 🌍 GLOBAL с градиентом от золотого к оранжевому
|
||||
prefixComponent = Component.text("🌍 ")
|
||||
.append(Component.text("G").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FFD700")))
|
||||
.append(Component.text("L").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FFA500")))
|
||||
.append(Component.text("O").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FF8C00")))
|
||||
.append(Component.text("B").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FF7F50")))
|
||||
.append(Component.text("A").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FF6347")))
|
||||
.append(Component.text("L").color(net.kyori.adventure.text.format.TextColor.fromHexString("#FF4500")));
|
||||
} else {
|
||||
// LOCAL с градиентом от голубого к синему (без эмодзи)
|
||||
prefixComponent = Component.text("L").color(net.kyori.adventure.text.format.TextColor.fromHexString("#87CEEB"))
|
||||
.append(Component.text("O").color(net.kyori.adventure.text.format.TextColor.fromHexString("#87CEFA")))
|
||||
.append(Component.text("C").color(net.kyori.adventure.text.format.TextColor.fromHexString("#00BFFF")))
|
||||
.append(Component.text("A").color(net.kyori.adventure.text.format.TextColor.fromHexString("#1E90FF")))
|
||||
.append(Component.text("L").color(net.kyori.adventure.text.format.TextColor.fromHexString("#4169E1")));
|
||||
}
|
||||
|
||||
// Используем displayName игрока напрямую (он уже Component с градиентом)
|
||||
Component playerComponent = createPlayerComponent(source, source.displayName());
|
||||
Component processedMessage = message;
|
||||
Component prefix = buildPrefix();
|
||||
Component separator = buildSeparator();
|
||||
Component player = buildPlayerComponent(source);
|
||||
|
||||
processedMessage = TextFormatter.formatMarkdown(processedMessage);
|
||||
processedMessage = processUrls(processedMessage);
|
||||
processedMessage = processEmojis(processedMessage);
|
||||
Component processed = message;
|
||||
processed = TextFormatter.formatMarkdown(processed);
|
||||
processed = processUrls(processed);
|
||||
processed = processEmojis(processed);
|
||||
|
||||
if (viewer instanceof Player viewerPlayer) {
|
||||
processedMessage = mentionHandler.processMentions(processedMessage, source, viewerPlayer);
|
||||
if (viewer instanceof Player vp) {
|
||||
processed = mentionHandler.processMentions(processed, source, vp);
|
||||
}
|
||||
|
||||
// Красивое разделение с градиентными стрелками
|
||||
Component separator = isGlobal ?
|
||||
Component.text(" ▶ ", net.kyori.adventure.text.format.TextColor.fromHexString("#FFD700")) :
|
||||
Component.text(" ▶ ", net.kyori.adventure.text.format.TextColor.fromHexString("#FFFFFF"));
|
||||
|
||||
return prefixComponent
|
||||
return prefix
|
||||
.append(separator)
|
||||
.append(playerComponent)
|
||||
.append(Component.text(": ", net.kyori.adventure.text.format.NamedTextColor.WHITE))
|
||||
.append(processedMessage);
|
||||
.append(player)
|
||||
.append(Component.text(": ", NamedTextColor.WHITE))
|
||||
.append(processed);
|
||||
}
|
||||
|
||||
private Component createPlayerComponent(Player player, Component displayName) {
|
||||
if (!plugin.getConfig().getBoolean("chat.hover.enabled", true)) {
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Префикс
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component buildPrefix() {
|
||||
String emoji = isGlobal ? cfg.getGlobalEmoji() : cfg.getLocalEmoji();
|
||||
String text = isGlobal ? cfg.getGlobalText() : cfg.getLocalText();
|
||||
List<String> colors = isGlobal ? cfg.getGlobalColors() : cfg.getLocalColors();
|
||||
|
||||
Component prefix = Component.empty();
|
||||
|
||||
if (!emoji.isEmpty()) {
|
||||
prefix = prefix.append(Component.text(emoji + " "));
|
||||
}
|
||||
|
||||
prefix = prefix.append(buildGradientText(text, colors));
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Строит компонент с плавным градиентом.
|
||||
* colors — список hex-цветов (минимум 1).
|
||||
* Если цветов меньше чем букв — интерполируем плавно между соседними цветами.
|
||||
* Если цветов больше чем букв — берём по одному на букву.
|
||||
*/
|
||||
private Component buildGradientText(String text, List<String> colors) {
|
||||
if (text.isEmpty()) return Component.empty();
|
||||
|
||||
if (colors.isEmpty()) {
|
||||
return Component.text(text, FALLBACK_COLOR);
|
||||
}
|
||||
|
||||
// Один цвет — просто красим весь текст
|
||||
if (colors.size() == 1) {
|
||||
return Component.text(text, parseColor(colors.get(0)));
|
||||
}
|
||||
|
||||
char[] chars = text.toCharArray();
|
||||
int len = chars.length;
|
||||
Component result = Component.empty();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
// Нормализованная позиция 0..1
|
||||
float t = len == 1 ? 0f : (float) i / (len - 1);
|
||||
|
||||
// Какой сегмент градиента
|
||||
float scaled = t * (colors.size() - 1);
|
||||
int seg = (int) scaled;
|
||||
float frac = scaled - seg;
|
||||
|
||||
// Зажимаем в пределах списка
|
||||
if (seg >= colors.size() - 1) {
|
||||
seg = colors.size() - 2;
|
||||
frac = 1f;
|
||||
}
|
||||
|
||||
TextColor from = parseColor(colors.get(seg));
|
||||
TextColor to = parseColor(colors.get(seg + 1));
|
||||
TextColor blended = blend(from, to, frac);
|
||||
|
||||
result = result.append(Component.text(String.valueOf(chars[i]), blended));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Линейная интерполяция между двумя TextColor */
|
||||
private static TextColor blend(TextColor from, TextColor to, float t) {
|
||||
int r = Math.round(from.red() + t * (to.red() - from.red()));
|
||||
int g = Math.round(from.green() + t * (to.green() - from.green()));
|
||||
int b = Math.round(from.blue() + t * (to.blue() - from.blue()));
|
||||
return TextColor.color(clamp(r), clamp(g), clamp(b));
|
||||
}
|
||||
|
||||
private static int clamp(int v) { return Math.max(0, Math.min(255, v)); }
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Разделитель
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component buildSeparator() {
|
||||
String text = isGlobal ? cfg.getGlobalSeparatorText() : cfg.getLocalSeparatorText();
|
||||
String color = isGlobal ? cfg.getGlobalSeparatorColor() : cfg.getLocalSeparatorColor();
|
||||
return Component.text(text, parseColor(color));
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Ник игрока с hover
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component buildPlayerComponent(Player player) {
|
||||
Component displayName = player.displayName();
|
||||
|
||||
if (!cfg.isHoverEnabled()) {
|
||||
return displayName.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " "));
|
||||
}
|
||||
|
||||
java.util.List<String> hoverLines = plugin.getConfig().getStringList("chat.hover.format");
|
||||
if (hoverLines.isEmpty()) {
|
||||
hoverLines = java.util.Arrays.asList(
|
||||
"<gradient:#FFD700:#FFA500><bold>▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬</bold></gradient>",
|
||||
"<gradient:#87CEEB:#4169E1><bold>👤 Информация об игроке</bold></gradient>",
|
||||
"",
|
||||
"<gray>🏷️ Имя: <white><bold>{player}</bold>",
|
||||
"<gray>📶 Пинг: <yellow><bold>{ping}ms</bold>",
|
||||
"<gray>❤️ Здоровье: <red><bold>{health}/20</bold>",
|
||||
"<gray>🍖 Голод: <gold><bold>{food}/20</bold>",
|
||||
"",
|
||||
"<gradient:#FFD700:#FFA500><bold>▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬</bold></gradient>",
|
||||
"<yellow>💬 <bold>Клик:</bold> <gray>написать личное сообщение"
|
||||
);
|
||||
}
|
||||
|
||||
List<String> hoverLines = cfg.getHoverFormat();
|
||||
net.kyori.adventure.text.TextComponent.Builder hoverBuilder = Component.text();
|
||||
|
||||
for (int i = 0; i < hoverLines.size(); i++) {
|
||||
String line = hoverLines.get(i);
|
||||
String line = hoverLines.get(i)
|
||||
.replace("{player}", player.getName())
|
||||
.replace("{world}", player.getWorld().getName())
|
||||
.replace("{ping}", String.valueOf(player.getPing()))
|
||||
.replace("{gamemode}", hm.getGamemodeName(player.getGameMode().name().toLowerCase()))
|
||||
.replace("{health}", String.valueOf(Math.round(player.getHealth())))
|
||||
.replace("{food}", String.valueOf(player.getFoodLevel()));
|
||||
|
||||
line = line.replace("{player}", player.getName())
|
||||
.replace("{world}", player.getWorld().getName())
|
||||
.replace("{ping}", String.valueOf(player.getPing()))
|
||||
.replace("{gamemode}", getGameModeDisplay(player.getGameMode()))
|
||||
.replace("{health}", String.valueOf(Math.round(player.getHealth())))
|
||||
.replace("{food}", String.valueOf(player.getFoodLevel()));
|
||||
|
||||
Component lineComponent = com.loki.lochat.utils.ChatFormatter.parse(line);
|
||||
hoverBuilder.append(lineComponent);
|
||||
|
||||
if (i < hoverLines.size() - 1) {
|
||||
hoverBuilder.append(Component.newline());
|
||||
}
|
||||
hoverBuilder.append(com.loki.lochat.utils.ChatFormatter.parse(line));
|
||||
if (i < hoverLines.size() - 1) hoverBuilder.append(Component.newline());
|
||||
}
|
||||
|
||||
return displayName
|
||||
|
|
@ -122,86 +182,43 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить красивое отображение игрового режима
|
||||
*/
|
||||
private String getGameModeDisplay(org.bukkit.GameMode gameMode) {
|
||||
return switch (gameMode) {
|
||||
case SURVIVAL -> "Выживание";
|
||||
case CREATIVE -> "Творчество";
|
||||
case ADVENTURE -> "Приключение";
|
||||
case SPECTATOR -> "Наблюдатель";
|
||||
};
|
||||
}
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// URL и эмодзи
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component processUrls(Component message) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
// Fixed regex: escape the dash in character class to avoid illegal range
|
||||
String urlPattern = "(https?://[\\w\\-.~:/?#\\[\\]@!$&'()*+,;=%]+)";
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(urlPattern);
|
||||
java.util.regex.Matcher matcher = pattern.matcher(plainText);
|
||||
String plain = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
java.util.regex.Pattern pattern =
|
||||
java.util.regex.Pattern.compile("(https?://[\\w\\-.~:/?#\\[\\]@!$&'()*+,;=%]+)");
|
||||
java.util.regex.Matcher matcher = pattern.matcher(plain);
|
||||
|
||||
Component result = message;
|
||||
while (matcher.find()) {
|
||||
String url = matcher.group(1);
|
||||
Component urlComponent = Component.text(url, NamedTextColor.AQUA, TextDecoration.UNDERLINED)
|
||||
Component urlComp = Component.text(url, NamedTextColor.AQUA, TextDecoration.UNDERLINED)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Открыть: " + url, NamedTextColor.GRAY)))
|
||||
.clickEvent(ClickEvent.openUrl(url));
|
||||
|
||||
result = result.replaceText(builder ->
|
||||
builder.matchLiteral(url).replacement(urlComponent)
|
||||
);
|
||||
result = result.replaceText(b -> b.matchLiteral(url).replacement(urlComp));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Component processEmojis(Component message) {
|
||||
Component result = message;
|
||||
|
||||
// Основные символы
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":heart:").replacement("♥"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":star:").replacement("★"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":check:").replacement("✓"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":cross:").replacement("✗"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":arrow:").replacement("→"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":skull:").replacement("☠"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":note:").replacement("♪"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":sun:").replacement("☀"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":moon:").replacement("☽"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":snowflake:").replacement("❄"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":fire:").replacement("▲"));
|
||||
|
||||
// Minecraft символы
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":diamond:").replacement("◆"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":sword:").replacement("⚔"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":pickaxe:").replacement("⛏"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":bow:").replacement("⌐"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":shield:").replacement("▣"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":potion:").replacement("⚗"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":book:").replacement("▤"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":crown:").replacement("♔"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":gem:").replacement("◊"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":emerald:").replacement("◈"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":gold:").replacement("◉"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":iron:").replacement("◎"));
|
||||
|
||||
// Дополнительные символы
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":thumbsup:").replacement("▲"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":thumbsdown:").replacement("▼"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":clap:").replacement("※"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":wave:").replacement("~"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":peace:").replacement("✌"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":ok:").replacement("◯"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":facepalm:").replacement("⌐_⌐"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":shrug:").replacement("¯\\_(ツ)_/¯"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":thinking:").replacement("?"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":laugh:").replacement("xD"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":cry:").replacement(":'("));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":rage:").replacement(">:("));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":cool:").replacement("B)"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":wink:").replacement(";)"));
|
||||
|
||||
for (java.util.Map.Entry<String, String> e : cfg.getEmojis().entrySet()) {
|
||||
result = result.replaceText(b -> b.matchLiteral(e.getKey()).replacement(e.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Утилиты
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private TextColor parseColor(String hex) {
|
||||
if (hex == null || hex.isBlank()) return FALLBACK_COLOR;
|
||||
String n = hex.startsWith("#") ? hex : "#" + hex;
|
||||
TextColor c = TextColor.fromHexString(n);
|
||||
return c != null ? c : FALLBACK_COLOR;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,12 +222,19 @@ public final class ChatFormatter {
|
|||
* Получает отображаемое имя игрока с градиентом
|
||||
*/
|
||||
private static String getPlayerDisplayName(Player player) {
|
||||
// Получаем экземпляр плагина
|
||||
com.loki.lochat.LoChat plugin = com.loki.lochat.LoChat.getInstance();
|
||||
try {
|
||||
// Получаем экземпляр плагина
|
||||
com.loki.lochat.LoChat plugin = com.loki.lochat.LoChat.getInstance();
|
||||
|
||||
// Если градиентный модуль включен, используем его
|
||||
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
||||
return plugin.getGradientModule().getFormattedName(player);
|
||||
// Если градиентный модуль включен, используем его
|
||||
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
||||
return plugin.getGradientModule().getFormattedName(player);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Если произошла ошибка, логируем и возвращаем обычное имя
|
||||
com.loki.lochat.LoChat.getInstance().getLogger().warning(
|
||||
"Ошибка при получении градиентного имени для " + player.getName() + ": " + e.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
// Иначе просто имя игрока
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
package com.loki.lochat.utils;
|
||||
|
||||
/**
|
||||
* Константы плагина
|
||||
* Константы плагина (только неизменяемые системные значения)
|
||||
*/
|
||||
public final class Constants {
|
||||
|
||||
// Чат
|
||||
public static final int DEFAULT_LOCAL_RADIUS = 100;
|
||||
public static final int CLEAR_CHAT_LINES = 100;
|
||||
// Время
|
||||
// Время (системные константы)
|
||||
public static final long MILLIS_IN_SECOND = 1000L;
|
||||
public static final int SECONDS_IN_MINUTE = 60;
|
||||
public static final int MINUTES_IN_HOUR = 60;
|
||||
public static final int HOURS_IN_DAY = 24;
|
||||
// Градиенты
|
||||
public static final int DEFAULT_PRICE_PER_COLOR = 50;
|
||||
public static final int DEFAULT_PREFIX_PRICE = 500;
|
||||
public static final int LUCKPERMS_PREFIX_PRIORITY = 100;
|
||||
// Folia/Paper совместимость
|
||||
|
||||
// Folia/Paper совместимость (системные константы)
|
||||
public static final int TICKS_PER_SECOND = 20;
|
||||
public static final int MILLIS_PER_TICK = 50;
|
||||
// Анти-спам
|
||||
|
||||
// Математические константы
|
||||
public static final int PERCENTAGE_MULTIPLIER = 100;
|
||||
|
||||
// LuckPerms приоритет (системная константа)
|
||||
public static final int LUCKPERMS_PREFIX_PRIORITY = 100;
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.loki.lochat.utils;
|
|||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TextFormatter {
|
||||
|
|
@ -14,82 +13,86 @@ public class TextFormatter {
|
|||
private static final Pattern STRIKETHROUGH_PATTERN = Pattern.compile("~~(.+?)~~");
|
||||
private static final Pattern CODE_PATTERN = Pattern.compile("`(.+?)`");
|
||||
|
||||
// Паттерн для быстрой проверки — есть ли в тексте markdown-символы
|
||||
private static final Pattern HAS_MARKDOWN = Pattern.compile("[*_~`]");
|
||||
|
||||
/**
|
||||
* Применяет markdown-форматирование к компоненту.
|
||||
*
|
||||
* Важно: PlainTextComponentSerializer стриппает все Adventure-цвета.
|
||||
* Поэтому мы обрабатываем только replaceText на оригинальном компоненте,
|
||||
* не конвертируя его в plain string и обратно.
|
||||
* Это сохраняет градиенты и цвета, добавленные Adventure.
|
||||
*/
|
||||
public static Component formatMarkdown(Component message) {
|
||||
String text = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
String plain = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
text = formatBold(text);
|
||||
text = formatItalic(text);
|
||||
text = formatUnderline(text);
|
||||
text = formatStrikethrough(text);
|
||||
text = formatCode(text);
|
||||
// Быстрая проверка — нет markdown-символов, возвращаем как есть (без потери цветов)
|
||||
if (!HAS_MARKDOWN.matcher(plain).find()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return ChatFormatter.parse(text);
|
||||
// Применяем замены через replaceText прямо на компоненте — цвета сохраняются
|
||||
message = applyBold(message);
|
||||
message = applyItalic(message);
|
||||
message = applyUnderline(message);
|
||||
message = applyStrikethrough(message);
|
||||
message = applyCode(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private static String formatBold(String text) {
|
||||
Matcher matcher = BOLD_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<bold>" + content + "</bold>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
private static Component applyBold(Component message) {
|
||||
return message.replaceText(config -> config
|
||||
.match(BOLD_PATTERN)
|
||||
.replacement((matchResult, builder) -> {
|
||||
String content = matchResult.group(1);
|
||||
return ChatFormatter.parse("<bold>" + content + "</bold>");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static String formatItalic(String text) {
|
||||
Matcher matcher = ITALIC_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
if (!content.startsWith("*") && !content.endsWith("*")) {
|
||||
matcher.appendReplacement(result, "<italic>" + content + "</italic>");
|
||||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
private static Component applyItalic(Component message) {
|
||||
return message.replaceText(config -> config
|
||||
.match(ITALIC_PATTERN)
|
||||
.replacement((matchResult, builder) -> {
|
||||
String content = matchResult.group(1);
|
||||
// Пропускаем если это часть ** (bold)
|
||||
if (content.startsWith("*") || content.endsWith("*")) {
|
||||
return builder.content(matchResult.group());
|
||||
}
|
||||
return ChatFormatter.parse("<italic>" + content + "</italic>");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static String formatUnderline(String text) {
|
||||
Matcher matcher = UNDERLINE_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<underlined>" + content + "</underlined>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
private static Component applyUnderline(Component message) {
|
||||
return message.replaceText(config -> config
|
||||
.match(UNDERLINE_PATTERN)
|
||||
.replacement((matchResult, builder) -> {
|
||||
String content = matchResult.group(1);
|
||||
return ChatFormatter.parse("<underlined>" + content + "</underlined>");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static String formatStrikethrough(String text) {
|
||||
Matcher matcher = STRIKETHROUGH_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<strikethrough>" + content + "</strikethrough>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
private static Component applyStrikethrough(Component message) {
|
||||
return message.replaceText(config -> config
|
||||
.match(STRIKETHROUGH_PATTERN)
|
||||
.replacement((matchResult, builder) -> {
|
||||
String content = matchResult.group(1);
|
||||
return ChatFormatter.parse("<strikethrough>" + content + "</strikethrough>");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private static String formatCode(String text) {
|
||||
Matcher matcher = CODE_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "󙀸`" + content + "`");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
private static Component applyCode(Component message) {
|
||||
return message.replaceText(config -> config
|
||||
.match(CODE_PATTERN)
|
||||
.replacement((matchResult, builder) -> {
|
||||
String content = matchResult.group(1);
|
||||
return ChatFormatter.parse("󙀸`" + content + "`");
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue