feat: LoHub Action system + Join system implementation
This commit is contained in:
parent
69cae99476
commit
6a4c8a7886
55 changed files with 3180 additions and 1022 deletions
|
|
@ -16,6 +16,10 @@ public class CustomCommand extends Command {
|
|||
public CustomCommand(String name, CustomCommandManager manager) {
|
||||
super(name);
|
||||
this.manager = manager;
|
||||
init(name);
|
||||
}
|
||||
|
||||
private void init(String name) {
|
||||
this.setDescription("Кастомная команда LoChat");
|
||||
this.setUsage("/" + name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ import org.bukkit.entity.Player;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Команда /nick для установки кастомного ника
|
||||
*/
|
||||
public class NickCommand implements CommandExecutor, TabCompleter {
|
||||
private final LoChat plugin;
|
||||
private final NickService nickService;
|
||||
|
|
@ -38,17 +35,14 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
|
||||
// /nick - показать текущий ник
|
||||
if (args.length == 0) {
|
||||
return showCurrentNick(player);
|
||||
}
|
||||
|
||||
// /nick reset - сбросить ник
|
||||
if (args[0].equalsIgnoreCase("reset")) {
|
||||
return resetNick(player);
|
||||
}
|
||||
|
||||
// /nick <ник> - установить ник
|
||||
String nickname = String.join(" ", args);
|
||||
return setNick(player, nickname);
|
||||
}
|
||||
|
|
@ -87,27 +81,23 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
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("• Поддержка цветов: <red>текст</red>, <gradient:#FF0000:#00FF00>текст</gradient>", 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) {
|
||||
|
|
@ -126,8 +116,22 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
completions.add("reset");
|
||||
completions.add("<ник>");
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
package com.loki.lochat.core.filter;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AdvancedMessageFilter {
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile(
|
||||
"(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,}"
|
||||
);
|
||||
|
||||
private final FileConfiguration config;
|
||||
private final List<String> whitelistedDomains;
|
||||
private final List<String> blacklistedDomains;
|
||||
|
||||
public AdvancedMessageFilter(FileConfiguration config) {
|
||||
this.config = config;
|
||||
this.whitelistedDomains = config.getStringList("filters.url.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.url.blacklist");
|
||||
}
|
||||
|
||||
public FilterResult filterMessage(Player player, String message) {
|
||||
if (config.getBoolean("filters.url.enabled", true)) {
|
||||
FilterResult urlResult = filterUrls(player, message);
|
||||
if (!urlResult.isAllowed()) {
|
||||
return urlResult;
|
||||
}
|
||||
message = urlResult.getFilteredMessage();
|
||||
}
|
||||
|
||||
if (config.getBoolean("filters.ip.enabled", true)) {
|
||||
FilterResult ipResult = filterIPs(player, message);
|
||||
if (!ipResult.isAllowed()) {
|
||||
return ipResult;
|
||||
}
|
||||
message = ipResult.getFilteredMessage();
|
||||
}
|
||||
|
||||
if (config.getBoolean("filters.repeat.enabled", true)) {
|
||||
message = filterRepeatingChars(message);
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
private FilterResult filterUrls(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.urlfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
Matcher matcher = URL_PATTERN.matcher(message);
|
||||
|
||||
while (matcher.find()) {
|
||||
String url = matcher.group();
|
||||
String domain = extractDomain(url);
|
||||
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean isWhitelisted = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (!isWhitelisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BСсылки запрещены!"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean isBlacklisted = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (isBlacklisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BЭта ссылка запрещена!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
private FilterResult filterIPs(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.ipfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String extractDomain(String url) {
|
||||
url = url.replaceFirst("^https?://", "");
|
||||
int slashIndex = url.indexOf('/');
|
||||
if (slashIndex != -1) {
|
||||
url = url.substring(0, slashIndex);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static class FilterResult {
|
||||
private final boolean allowed;
|
||||
private final String filteredMessage;
|
||||
private final String blockReason;
|
||||
|
||||
public FilterResult(boolean allowed, String filteredMessage, String blockReason) {
|
||||
this.allowed = allowed;
|
||||
this.filteredMessage = filteredMessage;
|
||||
this.blockReason = blockReason;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public String getFilteredMessage() {
|
||||
return filteredMessage;
|
||||
}
|
||||
|
||||
public String getBlockReason() {
|
||||
return blockReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,25 +9,29 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Реестр сервисов - Dependency Injection контейнер (SOLID DIP)
|
||||
*/
|
||||
public class ServiceRegistry {
|
||||
private final Map<Class<?>, Object> services = new HashMap<>();
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
registerServices();
|
||||
init();
|
||||
}
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin, ConfigManager configManager, MessageConfig messageConfig) {
|
||||
this.plugin = plugin;
|
||||
initWithDeps(configManager, messageConfig);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
registerServices();
|
||||
}
|
||||
|
||||
private void initWithDeps(ConfigManager configManager, MessageConfig messageConfig) {
|
||||
registerServicesWithDeps(configManager, messageConfig);
|
||||
}
|
||||
|
||||
private void registerServices() {
|
||||
// Регистрируем все сервисы (базовая версия)
|
||||
register(ChatService.class, new ChatServiceImpl(plugin, this));
|
||||
register(MuteService.class, new MuteServiceImpl(plugin));
|
||||
register(CooldownService.class, new CooldownServiceImpl());
|
||||
|
|
@ -36,14 +40,12 @@ 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());
|
||||
register(MessageService.class, new MessageServiceImpl(plugin, this));
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin));
|
||||
|
||||
// Новые сервисы
|
||||
register(PMService.class, new PMServiceImpl());
|
||||
register(IgnoreService.class, new IgnoreServiceImpl(plugin));
|
||||
register(SpyService.class, new SpyServiceImpl(plugin, messageConfig));
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.loki.lochat.listener;
|
||||
|
||||
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.util.FoliaUtil;
|
||||
import com.loki.lochat.utils.MentionHandler;
|
||||
import com.loki.lochat.utils.TextFormatter;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
|
|
@ -12,24 +14,23 @@ import net.kyori.adventure.text.event.HoverEvent;
|
|||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Слушатель событий чата с Paper Chat API
|
||||
* Поддерживает hover tooltips, click actions, mentions
|
||||
*/
|
||||
public class ChatEventListener implements Listener {
|
||||
private final JavaPlugin plugin;
|
||||
private final MessageService messageService;
|
||||
private final AdvancedMessageFilter advancedFilter;
|
||||
private final MentionHandler mentionHandler;
|
||||
|
||||
public ChatEventListener(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.messageService = registry.get(MessageService.class);
|
||||
this.advancedFilter = new AdvancedMessageFilter(plugin.getConfig());
|
||||
this.mentionHandler = new MentionHandler(plugin);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
|
|
@ -37,7 +38,6 @@ public class ChatEventListener implements Listener {
|
|||
Player sender = event.getPlayer();
|
||||
String message = PlainTextComponentSerializer.plainText().serialize(event.message());
|
||||
|
||||
// Проверяем фильтры (мут, кулдаун)
|
||||
boolean allowed = messageService.processMessage(sender, message);
|
||||
|
||||
if (!allowed) {
|
||||
|
|
@ -45,13 +45,20 @@ public class ChatEventListener implements Listener {
|
|||
return;
|
||||
}
|
||||
|
||||
// Устанавливаем кастомный рендерер
|
||||
AdvancedMessageFilter.FilterResult filterResult = advancedFilter.filterMessage(sender, message);
|
||||
|
||||
if (!filterResult.isAllowed()) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(filterResult.getBlockReason()));
|
||||
return;
|
||||
}
|
||||
|
||||
String filteredMessage = filterResult.getFilteredMessage();
|
||||
event.message(Component.text(filteredMessage));
|
||||
|
||||
event.renderer(new EnhancedChatRenderer(sender, event.message()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Продвинутый рендерер чата с hover/click/mentions
|
||||
*/
|
||||
private class EnhancedChatRenderer implements ChatRenderer {
|
||||
private final Player sender;
|
||||
private final Component originalMessage;
|
||||
|
|
@ -63,47 +70,31 @@ public class ChatEventListener implements Listener {
|
|||
|
||||
@Override
|
||||
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
|
||||
// Получаем конфиг формата
|
||||
String format = plugin.getConfig().getString("chat.global.format", "{prefix} {player}: {message}");
|
||||
String prefix = plugin.getConfig().getString("chat.global.prefix", "[G]");
|
||||
|
||||
// Создаём компонент с hover и click
|
||||
Component playerComponent = createPlayerComponent(source, sourceDisplayName);
|
||||
|
||||
// Обрабатываем сообщение
|
||||
Component processedMessage = message;
|
||||
|
||||
// 1. Обрабатываем URL (делаем кликабельными)
|
||||
processedMessage = TextFormatter.formatMarkdown(processedMessage);
|
||||
processedMessage = processUrls(processedMessage);
|
||||
|
||||
// 2. Обрабатываем эмодзи шорткоды (:smile: -> 😊)
|
||||
processedMessage = processEmojis(processedMessage);
|
||||
|
||||
// 3. Обрабатываем упоминания если viewer это Player
|
||||
if (viewer instanceof Player viewerPlayer) {
|
||||
processedMessage = processMentions(processedMessage, viewerPlayer);
|
||||
processedMessage = mentionHandler.processMentions(processedMessage, source, viewerPlayer);
|
||||
}
|
||||
|
||||
// Собираем финальное сообщение
|
||||
return Component.text(prefix + " ")
|
||||
.append(playerComponent)
|
||||
.append(Component.text(": "))
|
||||
.append(processedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт компонент имени игрока с hover и click
|
||||
*/
|
||||
private Component createPlayerComponent(Player player, Component displayName) {
|
||||
// Проверяем включен ли hover
|
||||
if (!plugin.getConfig().getBoolean("chat.hover.enabled", true)) {
|
||||
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(
|
||||
"<gray>Игрок: <white>{player}",
|
||||
"<gray>Мир: <green>{world}",
|
||||
|
|
@ -112,12 +103,10 @@ public class ChatEventListener implements Listener {
|
|||
);
|
||||
}
|
||||
|
||||
// Строим hover text
|
||||
net.kyori.adventure.text.TextComponent.Builder hoverBuilder = Component.text();
|
||||
for (int i = 0; i < hoverLines.size(); i++) {
|
||||
String line = hoverLines.get(i);
|
||||
|
||||
// Заменяем плейсхолдеры
|
||||
line = line.replace("{player}", player.getName())
|
||||
.replace("{world}", player.getWorld().getName())
|
||||
.replace("{ping}", String.valueOf(player.getPing()))
|
||||
|
|
@ -125,62 +114,28 @@ public class ChatEventListener implements Listener {
|
|||
.replace("{health}", String.valueOf(Math.round(player.getHealth())))
|
||||
.replace("{food}", String.valueOf(player.getFoodLevel()));
|
||||
|
||||
// Парсим MiniMessage
|
||||
Component lineComponent = com.loki.lochat.utils.ChatFormatter.parse(line);
|
||||
hoverBuilder.append(lineComponent);
|
||||
|
||||
// Добавляем перенос строки если не последняя
|
||||
if (i < hoverLines.size() - 1) {
|
||||
hoverBuilder.append(Component.newline());
|
||||
}
|
||||
}
|
||||
|
||||
// Click: открываем /msg
|
||||
return displayName
|
||||
.hoverEvent(HoverEvent.showText(hoverBuilder.build()))
|
||||
.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает упоминания (@player) в сообщении
|
||||
*/
|
||||
private Component processMentions(Component message, Player viewer) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
// Проверяем упоминание текущего игрока
|
||||
if (plainText.toLowerCase().contains("@" + viewer.getName().toLowerCase())) {
|
||||
// Подсвечиваем упоминание
|
||||
Component highlighted = message.replaceText(builder ->
|
||||
builder.matchLiteral("@" + viewer.getName())
|
||||
.replacement(Component.text("@" + viewer.getName(), NamedTextColor.YELLOW, TextDecoration.BOLD))
|
||||
);
|
||||
|
||||
// Воспроизводим звук уведомления
|
||||
FoliaUtil.runEntityTask(plugin, viewer, () -> {
|
||||
viewer.playSound(viewer.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.5f);
|
||||
});
|
||||
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает URL в сообщении (делает кликабельными)
|
||||
*/
|
||||
private Component processUrls(Component message) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
// Простой regex для URL
|
||||
String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!$&'()*+,;=%]+)";
|
||||
String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!35538117-c5a8-4bfe-b9f2-b0dcf9374222'()*+,;=%]+)";
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(urlPattern);
|
||||
java.util.regex.Matcher matcher = pattern.matcher(plainText);
|
||||
|
||||
Component result = message;
|
||||
while (matcher.find()) {
|
||||
String url = matcher.group(1);
|
||||
// Делаем URL кликабельным с hover
|
||||
Component urlComponent = Component.text(url, NamedTextColor.AQUA, TextDecoration.UNDERLINED)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Открыть: " + url, NamedTextColor.GRAY)))
|
||||
.clickEvent(ClickEvent.openUrl(url));
|
||||
|
|
@ -193,35 +148,28 @@ public class ChatEventListener implements Listener {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает эмодзи шорткоды (:smile: -> ☺)
|
||||
* Использует только MC-совместимые Unicode символы
|
||||
*/
|
||||
private Component processEmojis(Component message) {
|
||||
Component result = message;
|
||||
|
||||
// MC-совместимые Unicode эмодзи
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":smile:").replacement("☺"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":heart:").replacement("♥"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":fire:").replacement("※"));
|
||||
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(":skull:").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(":peace:").replacement("☮"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":yin:").replacement("☯"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":flower:").replacement("✿"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":moon:").replacement("☽"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":snowflake:").replacement("❄"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":lightning:").replacement("⚡"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":crown:").replacement("♔"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":sword:").replacement("⚔"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":shield:").replacement("⛨"));
|
||||
result = result.replaceText(builder -> builder.matchLiteral(":fire:").replacement("🔥"));
|
||||
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("💎"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ public class CustomCommandManager {
|
|||
public CustomCommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.commands = new HashMap<>();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
loadCommands();
|
||||
}
|
||||
|
||||
|
|
|
|||
97
src/main/java/com/loki/lochat/managers/EmojiManager.java
Normal file
97
src/main/java/com/loki/lochat/managers/EmojiManager.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EmojiManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, String> emojis = new HashMap<>();
|
||||
private boolean enabled;
|
||||
private boolean requirePermission;
|
||||
|
||||
public EmojiManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
File file = new File(plugin.getDataFolder(), "emojis.yml");
|
||||
if (!file.exists()) {
|
||||
plugin.saveResource("emojis.yml", false);
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
emojis.clear();
|
||||
|
||||
// Настройки
|
||||
enabled = config.getBoolean("settings.enabled", true);
|
||||
requirePermission = config.getBoolean("settings.require-permission", false);
|
||||
|
||||
// Загружаем все категории
|
||||
for (String category : config.getKeys(false)) {
|
||||
if (category.equals("settings")) continue;
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection(category);
|
||||
if (section != null) {
|
||||
for (String key : section.getKeys(false)) {
|
||||
String value = section.getString(key);
|
||||
if (value != null) {
|
||||
emojis.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Загружено " + emojis.size() + " смайликов");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет смайлики в сообщении
|
||||
*/
|
||||
public String process(String message, Player player) {
|
||||
if (!enabled) return message;
|
||||
|
||||
// Проверка права
|
||||
if (requirePermission && player != null && !player.hasPermission("chat.emoji.use")) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String process(String message) {
|
||||
if (!enabled) return message;
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, String> getEmojis() {
|
||||
return emojis;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
106
src/main/java/com/loki/lochat/utils/MentionHandler.java
Normal file
106
src/main/java/com/loki/lochat/utils/MentionHandler.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package com.loki.lochat.utils;
|
||||
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MentionHandler {
|
||||
|
||||
private static final Pattern EVERYONE_PATTERN = Pattern.compile("@everyone\\b");
|
||||
private static final Pattern HERE_PATTERN = Pattern.compile("@here\\b");
|
||||
private static final Pattern ROLE_PATTERN = Pattern.compile("@role:([\\w.]+)");
|
||||
private static final Pattern PLAYER_PATTERN = Pattern.compile("@(\\w+)");
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public MentionHandler(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public Component processMentions(Component message, Player sender, Player viewer) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
List<Player> mentionedPlayers = new ArrayList<>();
|
||||
|
||||
if (sender.hasPermission("lochat.mention.everyone")) {
|
||||
Matcher everyoneMatcher = EVERYONE_PATTERN.matcher(plainText);
|
||||
if (everyoneMatcher.find()) {
|
||||
mentionedPlayers.addAll(Bukkit.getOnlinePlayers());
|
||||
message = highlightMention(message, "@everyone");
|
||||
}
|
||||
}
|
||||
|
||||
if (sender.hasPermission("lochat.mention.here")) {
|
||||
Matcher hereMatcher = HERE_PATTERN.matcher(plainText);
|
||||
if (hereMatcher.find()) {
|
||||
mentionedPlayers.addAll(Bukkit.getOnlinePlayers());
|
||||
message = highlightMention(message, "@here");
|
||||
}
|
||||
}
|
||||
|
||||
if (sender.hasPermission("lochat.mention.role")) {
|
||||
Matcher roleMatcher = ROLE_PATTERN.matcher(plainText);
|
||||
while (roleMatcher.find()) {
|
||||
String permission = roleMatcher.group(1);
|
||||
String fullMention = "@role:" + permission;
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasPermission(permission)) {
|
||||
mentionedPlayers.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
message = highlightMention(message, fullMention);
|
||||
}
|
||||
}
|
||||
|
||||
Matcher playerMatcher = PLAYER_PATTERN.matcher(plainText);
|
||||
while (playerMatcher.find()) {
|
||||
String playerName = playerMatcher.group(1);
|
||||
Player mentioned = Bukkit.getPlayerExact(playerName);
|
||||
|
||||
if (mentioned != null && mentioned.isOnline()) {
|
||||
mentionedPlayers.add(mentioned);
|
||||
message = highlightMention(message, "@" + playerName);
|
||||
}
|
||||
}
|
||||
|
||||
if (mentionedPlayers.contains(viewer)) {
|
||||
playMentionSound(viewer);
|
||||
message = highlightForViewer(message, viewer);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private Component highlightMention(Component message, String mention) {
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(mention)
|
||||
.replacement(Component.text(mention, NamedTextColor.YELLOW, TextDecoration.BOLD))
|
||||
);
|
||||
}
|
||||
|
||||
private Component highlightForViewer(Component message, Player viewer) {
|
||||
String viewerMention = "@" + viewer.getName();
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(viewerMention)
|
||||
.replacement(Component.text(viewerMention, NamedTextColor.GOLD, TextDecoration.BOLD))
|
||||
);
|
||||
}
|
||||
|
||||
private void playMentionSound(Player player) {
|
||||
FoliaUtil.runEntityTask(plugin, player, () -> {
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.5f);
|
||||
});
|
||||
}
|
||||
}
|
||||
95
src/main/java/com/loki/lochat/utils/TextFormatter.java
Normal file
95
src/main/java/com/loki/lochat/utils/TextFormatter.java
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
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 {
|
||||
|
||||
private static final Pattern BOLD_PATTERN = Pattern.compile("\\*\\*(.+?)\\*\\*");
|
||||
private static final Pattern ITALIC_PATTERN = Pattern.compile("\\*(.+?)\\*");
|
||||
private static final Pattern UNDERLINE_PATTERN = Pattern.compile("__(.+?)__");
|
||||
private static final Pattern STRIKETHROUGH_PATTERN = Pattern.compile("~~(.+?)~~");
|
||||
private static final Pattern CODE_PATTERN = Pattern.compile("`(.+?)`");
|
||||
|
||||
public static Component formatMarkdown(Component message) {
|
||||
String text = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
text = formatBold(text);
|
||||
text = formatItalic(text);
|
||||
text = formatUnderline(text);
|
||||
text = formatStrikethrough(text);
|
||||
text = formatCode(text);
|
||||
|
||||
return ChatFormatter.parse(text);
|
||||
}
|
||||
|
||||
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 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 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 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 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
settings:
|
||||
# Префикс автосообщений
|
||||
prefix: "<gradient:#00D4FF:#0099CC>[INFO]</gradient>"
|
||||
prefix: "�D4FF[�D4FFI�CAF7N�C0EEF�B7E6OcCC]"
|
||||
|
||||
# Режим отправки: sequential (по порядку), random (случайно), custom (свой порядок)
|
||||
mode: "sequential"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ config-version: 3
|
|||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Префикс плагина в сообщениях
|
||||
prefix: "<gradient:#00D4FF:#0099CC>[LoChat]</gradient>"
|
||||
prefix: "�D4FF[�D4FFL�CAF7o�C0EEC�B7E6h�ADDDa�A3D5tcCC]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ЧАТА │
|
||||
|
|
@ -30,9 +30,8 @@ chat:
|
|||
# Кулдаун между сообщениями (секунды)
|
||||
cooldown: 3
|
||||
# Префикс глобального чата
|
||||
prefix: "<gradient:#FFD700:#FFA500>[G]</gradient>"
|
||||
# Формат сообщения: {prefix} {player} {message}
|
||||
format: "{prefix} <gradient:#00FF88:#00CC66>{player}</gradient><dark_gray>:</dark_gray> {message}"
|
||||
prefix: "&#FFD700[G]"
|
||||
format: "{prefix} �FF88{player}򇨣: {message}"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# ЛОКАЛЬНЫЙ ЧАТ
|
||||
|
|
@ -45,9 +44,8 @@ chat:
|
|||
# Кулдаун между сообщениями (секунды)
|
||||
cooldown: 1
|
||||
# Префикс локального чата
|
||||
prefix: "<gradient:#87CEEB:#4682B4>[L]</gradient>"
|
||||
# Формат сообщения
|
||||
format: "{prefix} <gradient:#00FF88:#00CC66>{player}</gradient><dark_gray>:</dark_gray> {message}"
|
||||
prefix: "WCEEB[L]"
|
||||
format: "{prefix} �FF88{player}򇨣: {message}"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# ЛИЧНЫЕ СООБЩЕНИЯ
|
||||
|
|
@ -111,9 +109,8 @@ mentions:
|
|||
# Тип звука
|
||||
sound-type: "BLOCK_NOTE_BLOCK_PLING"
|
||||
# Подсветка упоминания в чате
|
||||
highlight: "<gradient:#FFFF00:#FFD700>@{player}</gradient>"
|
||||
# Подсветка собственного ника
|
||||
self-highlight: "<gradient:#FFD700:#FFA500>{player}</gradient>"
|
||||
highlight: "&#FFFF00@{player}"
|
||||
self-highlight: "&#FFD700{player}"
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АВТОСООБЩЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
|
@ -134,7 +131,7 @@ announcements:
|
|||
# Показывать title при объявлении
|
||||
show-title: true
|
||||
# Заголовок title
|
||||
title-header: "<gradient:#FF6B6B:#FF4757>ОБЪЯВЛЕНИЕ</gradient>"
|
||||
title-header: "&#FF6B6BОБЪЯВЛЕНИЕ"
|
||||
# Показывать actionbar при объявлении
|
||||
show-actionbar: true
|
||||
# Время показа title (в секундах)
|
||||
|
|
@ -142,7 +139,7 @@ announcements:
|
|||
|
||||
# Формат объявления
|
||||
formats:
|
||||
announcement: "<gradient:#FF6B6B:#FF4757>[ОБЪЯВЛЕНИЕ]</gradient> <message>"
|
||||
announcement: "&#FF6B6B[ОБЪЯВЛЕНИЕ] <message>"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОЧИСТКА ЧАТА │
|
||||
|
|
@ -153,7 +150,7 @@ clearchat:
|
|||
# Показывать сообщение после очистки чата
|
||||
enabled: true
|
||||
# Текст сообщения ({player} - имя администратора)
|
||||
text: "<gradient:#FF6B6B:#FF4757>Чат очищен администратором {player}</gradient>"
|
||||
text: "&#FF6B6BЧат очищен администратором {player}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГОЛОВЫ ИГРОКОВ │
|
||||
|
|
@ -179,20 +176,14 @@ mute:
|
|||
# %operator% - модератор
|
||||
|
||||
messages:
|
||||
# Сообщение при муте (broadcast)
|
||||
muted: "<gradient:#FF6B6B:#FF4757>%player%</gradient> <gray>был замучен на</gray> <gradient:#FF6B6B:#FF4757>%duration%</gradient> <gray>модератором</gray> <gradient:#FF6B6B:#FF4757>%operator%</gradient><gray>. Причина:</gray> <gradient:#FF6B6B:#FF4757>%reason%</gradient>"
|
||||
# Сообщение при размуте (broadcast)
|
||||
unmuted: "<gradient:#6BCB77:#4ECDC4>%player%</gradient> <gray>был размучен модератором</gray> <gradient:#6BCB77:#4ECDC4>%operator%</gradient>"
|
||||
# Сообщение замученному игроку
|
||||
you-muted: "<gradient:#FF6B6B:#FF4757>Вы замучены на %duration%! Причина: %reason%</gradient>"
|
||||
# Сообщение при перманентном муте
|
||||
you-muted-permanent: "<gradient:#FF6B6B:#FF4757>Вы замучены навсегда! Причина: %reason%</gradient>"
|
||||
# Сообщение при попытке написать в чат
|
||||
cannot-chat: "<gradient:#FF6B6B:#FF4757>Вы замучены! Осталось: %duration%</gradient>"
|
||||
cannot-chat-permanent: "<gradient:#FF6B6B:#FF4757>Вы замучены навсегда!</gradient>"
|
||||
# Тихий мут (только для тех кто видит)
|
||||
silent-muted: "<gray>[Тихо]</gray> <gradient:#FF6B6B:#FF4757>%player%</gradient> <gray>замучен на</gray> <gradient:#FF6B6B:#FF4757>%duration%</gradient> <gray>(%operator%). Причина: %reason%</gray>"
|
||||
silent-unmuted: "<gray>[Тихо]</gray> <gradient:#6BCB77:#4ECDC4>%player%</gradient> <gray>размучен (%operator%)</gray>"
|
||||
muted: "&#FF6B6B%player% 󙀸был замучен на &#FF6B6B%duration% 󙀸модератором &#FF6B6B%operator%󙀸. Причина: &#FF6B6B%reason%"
|
||||
unmuted: "BCB77%player% 󙀸был размучен модератором BCB77%operator%"
|
||||
you-muted: "&#FF6B6BВы замучены на %duration%! Причина: %reason%"
|
||||
you-muted-permanent: "&#FF6B6BВы замучены навсегда! Причина: %reason%"
|
||||
cannot-chat: "&#FF6B6BВы замучены! Осталось: %duration%"
|
||||
cannot-chat-permanent: "&#FF6B6BВы замучены навсегда!"
|
||||
silent-muted: "󙀸[Тихо] &#FF6B6B%player% 󙀸замучен на &#FF6B6B%duration% 󙀸(%operator%). Причина: %reason%"
|
||||
silent-unmuted: "󙀸[Тихо] BCB77%player% 󙀸размучен (%operator%)"
|
||||
|
||||
# Причина по умолчанию если не указана
|
||||
default-reason: "Нарушение правил"
|
||||
|
|
@ -212,4 +203,46 @@ mute:
|
|||
- "7d"
|
||||
- "14d"
|
||||
- "30d"
|
||||
- "perm"
|
||||
- "perm"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФИЛЬТРЫ СООБЩЕНИЙ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
filters:
|
||||
url:
|
||||
enabled: true
|
||||
whitelist:
|
||||
- "youtube.com"
|
||||
- "youtu.be"
|
||||
- "discord.gg"
|
||||
- "twitch.tv"
|
||||
blacklist:
|
||||
- "scam-site.com"
|
||||
blocked-message: "&#FF6B6BС&#FF5F5Fс&#FF5353ы&#FF4747л&#FF3B3Bк&#FF2F2Fи &#FF2323з&#FF1717а&#FF0B0Bп&#FF0000р&#FF0000е&#FF0000щ&#FF0000е&#FF0000н&#FF0000ы!"
|
||||
|
||||
ip:
|
||||
enabled: true
|
||||
block: false
|
||||
replacement: "[IP скрыт]"
|
||||
blocked-message: "&#FF6B6BI&#FF5F5FP &#FF5353а&#FF4747д&#FF3B3Bр&#FF2F2Fе&#FF2323с&#FF1717а &#FF0B0Bз&#FF0000а&#FF0000п&#FF0000р&#FF0000е&#FF0000щ&#FF0000е&#FF0000н&#FF0000ы!"
|
||||
|
||||
repeat:
|
||||
enabled: true
|
||||
max-repeats: 3
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ УПОМИНАНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
advanced-mentions:
|
||||
enabled: true
|
||||
everyone:
|
||||
enabled: true
|
||||
permission: "lochat.mention.everyone"
|
||||
here:
|
||||
enabled: true
|
||||
permission: "lochat.mention.here"
|
||||
role:
|
||||
enabled: true
|
||||
permission: "lochat.mention.role"
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Префикс градиентного модуля
|
||||
prefix: "<gradient:#9370DB:#8A2BE2>[Gradient]</gradient>"
|
||||
prefix: "⒚DB[⒚DBGE6CD9r΀AD7aℓD5dF65D3iA62D1eᶈCFnˁDCDtB5BCB]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КОМАНДА /color │
|
||||
|
|
|
|||
|
|
@ -9,23 +9,23 @@
|
|||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Префикс плагина
|
||||
prefix: "<gradient:#00D4FF:#0099CC>[LoChat]</gradient>"
|
||||
prefix: "�D4FF[�D4FFL�CAF7o�C0EEC�B7E6h�ADDDa�A3D5tcCC]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГЛОБАЛЬНЫЙ ЧАТ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
global:
|
||||
disabled: "<gradient:#FF6B6B:#FF4757>Глобальный чат отключен</gradient>"
|
||||
toggled-on: "<gradient:#6BCB77:#4ECDC4>Глобальный чат включен</gradient>"
|
||||
toggled-off: "<gradient:#FF6B6B:#FF4757>Глобальный чат отключен</gradient>"
|
||||
disabled: "&#FF6B6BГ&#FF5F5Fл&#FF5353о&#FF4747б&#FF3B3Bа&#FF2F2Fл&#FF2323ь&#FF1717н&#FF0B0Bы&#FF0000й &#FF0000ч&#FF0000а&#FF0000т &#FF0000о&#FF0000т&#FF0000к&#FF0000л&#FF0000ю&#FF0000ч&#FF0000е&#FF0000н"
|
||||
toggled-on: "BCB77Г>C973лAC76Fо3C56Bб1C367а(C163л&BF5FьFBD5BнBB57ыEB953й B74F ч�DB54BаB347т B347вB347кB347лB347юB347чB347еB347н"
|
||||
toggled-off: "&#FF6B6BГ&#FF5F5Fл&#FF5353о&#FF4747б&#FF3B3Bа&#FF2F2Fл&#FF2323ь&#FF1717н&#FF0B0Bы&#FF0000й &#FF0000ч&#FF0000а&#FF0000т &#FF0000о&#FF0000т&#FF0000к&#FF0000л&#FF0000ю&#FF0000ч&#FF0000е&#FF0000н"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЛОКАЛЬНЫЙ ЧАТ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
local:
|
||||
no-players: "<gradient:#FFA726:#FF9800>Рядом нет игроков</gradient>"
|
||||
no-players: "&#FFA726Р&#FFA322я&#FF9F1Eд&#FF9B1Aо&#FF9716м &#FF9312н&#FF8F0Eе&#FF8B0Aт &#FF8706и&#FF8302г&#FF8000р&#FF8000о&#FF8000к&#FF8000о&#FF8000в"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЛИЧНЫЕ СООБЩЕНИЯ │
|
||||
|
|
@ -47,36 +47,36 @@ pm:
|
|||
received: "<gray>[PM]</gray> <white>{player}</white> <gray>→</gray> <gradient:#87CEEB:#4682B4>Ты</gradient><gray>:</gray> {message}"
|
||||
|
||||
# Ошибки PM
|
||||
no-reply: "<gradient:#FF6B6B:#FF4757>Некому ответить</gradient>"
|
||||
ignored: "<gradient:#FF6B6B:#FF4757>Этот игрок вас игнорирует</gradient>"
|
||||
player-offline: "<gradient:#FF6B6B:#FF4757>Игрок не в сети</gradient>"
|
||||
self: "<gradient:#FF6B6B:#FF4757>Нельзя писать самому себе</gradient>"
|
||||
no-reply: "&#FF6B6BН&#FF5F5Fе&#FF5353к&#FF4747о&#FF3B3Bм&#FF2F2Fу &#FF2323о&#FF1717т&#FF0B0Bв&#FF0000е&#FF0000т&#FF0000и&#FF0000т&#FF0000ь"
|
||||
ignored: "&#FF6B6BЭ&#FF5F5Fт&#FF5353о&#FF4747т &#FF3B3Bи&#FF2F2Fг&#FF2323р&#FF1717о&#FF0B0Bк &#FF0000в&#FF0000а&#FF0000с &#FF0000и&#FF0000г&#FF0000н&#FF0000о&#FF0000р&#FF0000и&#FF0000р&#FF0000у&#FF0000е&#FF0000т"
|
||||
player-offline: "&#FF6B6BИ&#FF5F5Fг&#FF5353р&#FF4747о&#FF3B3Bк &#FF2F2Fн&#FF2323е &#FF1717в &#FF0B0Bс&#FF0000е&#FF0000т&#FF0000и"
|
||||
self: "&#FF6B6BН&#FF5F5Fе&#FF5353л&#FF4747ь&#FF3B3Bз&#FF2F2Fя &#FF2323п&#FF1717и&#FF0B0Bс&#FF0000а&#FF0000т&#FF0000ь &#FF0000с&#FF0000а&#FF0000м&#FF0000о&#FF0000м&#FF0000у &#FF0000с&#FF0000е&#FF0000б&#FF0000е"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ИГНОРИРОВАНИЕ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
ignore:
|
||||
added: "<gradient:#6BCB77:#4ECDC4>Игрок {player} добавлен в игнор</gradient>"
|
||||
removed: "<gradient:#6BCB77:#4ECDC4>Игрок {player} убран из игнора</gradient>"
|
||||
already-ignored: "<gradient:#FFA726:#FF9800>Игрок уже в игноре</gradient>"
|
||||
not-ignored: "<gradient:#FFA726:#FF9800>Игрок не в игноре</gradient>"
|
||||
self: "<gradient:#FF6B6B:#FF4757>Нельзя игнорировать себя</gradient>"
|
||||
added: "BCB77И>C973гAC76Fр3C56Bо1C367к (C163{&BF5FpFBD5BlBB57aEB953yB74Fe�DB54BrB347} B347дB347оB347бB347аB347вB347лB347еB347н B347в B347иB347гB347нB347оB347р"
|
||||
removed: "BCB77И>C973гAC76Fр3C56Bо1C367к (C163{&BF5FpFBD5BlBB57aEB953yB74Fe�DB54BrB347} B347уB347бB347рB347аB347н B347иB347з B347иB347гB347нB347оB347рB347а"
|
||||
already-ignored: "&#FFA726И&#FFA322г&#FF9F1Eр&#FF9B1Aо&#FF9716к &#FF9312у&#FF8F0Eж&#FF8B0Aе &#FF8706в &#FF8302и&#FF8000г&#FF8000н&#FF8000о&#FF8000р&#FF8000е"
|
||||
not-ignored: "&#FFA726И&#FFA322г&#FF9F1Eр&#FF9B1Aо&#FF9716к &#FF9312н&#FF8F0Eе &#FF8B0Aв &#FF8706и&#FF8302г&#FF8000н&#FF8000о&#FF8000р&#FF8000е"
|
||||
self: "&#FF6B6BН&#FF5F5Fе&#FF5353л&#FF4747ь&#FF3B3Bз&#FF2F2Fя &#FF2323и&#FF1717г&#FF0B0Bн&#FF0000о&#FF0000р&#FF0000и&#FF0000р&#FF0000о&#FF0000в&#FF0000а&#FF0000т&#FF0000ь &#FF0000с&#FF0000е&#FF0000б&#FF0000я"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОБЪЯВЛЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
announce:
|
||||
reloaded: "<gradient:#6BCB77:#4ECDC4>Конфигурация перезагружена</gradient>"
|
||||
reloaded: "BCB77К>C973оAC76Fн3C56Bф1C367и(C163г&BF5FуFBD5BрBB57аEB953цB74Fи�DB54Bя B347пB347еB347рB347еB347зB347аB347гB347рB347уB347жB347еB347нB347а"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ШПИОН РЕЖИМ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
spy:
|
||||
enabled: "<gradient:#6BCB77:#4ECDC4>Шпион режим включен</gradient>"
|
||||
disabled: "<gradient:#FF6B6B:#FF4757>Шпион режим выключен</gradient>"
|
||||
enabled: "BCB77Ш>C973пAC76Fи3C56Bо1C367н (C163р&BF5FеFBD5BжBB57иEB953м B74Fв�DB54BкB347лB347юB347чB347еB347н"
|
||||
disabled: "&#FF6B6BШ&#FF5F5Fп&#FF5353и&#FF4747о&#FF3B3Bн &#FF2F2Fр&#FF2323е&#FF1717ж&#FF0B0Bи&#FF0000м &#FF0000в&#FF0000ы&#FF0000к&#FF0000л&#FF0000ю&#FF0000ч&#FF0000е&#FF0000н"
|
||||
format: "<gray>[SPY]</gray> <gradient:#FFD700:#FFA500>{sender}</gradient> <gray>→</gray> <gradient:#87CEEB:#4682B4>{receiver}</gradient><gray>:</gray> <white>{message}</white>"
|
||||
chat-format: "<gray>[SPY]</gray> <gradient:#FFA726:#FF9800>{type}</gradient> <gradient:#00FF88:#00CC66>{sender}</gradient><gray>:</gray> <white>{message}</white>"
|
||||
|
||||
|
|
@ -85,49 +85,49 @@ spy:
|
|||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
filter:
|
||||
blocked: "<gradient:#FF6B6B:#FF4757>Сообщение заблокировано (недопустимые слова)</gradient>"
|
||||
warning: "<gradient:#FFA726:#FF9800>Предупреждение: не используйте недопустимые слова!</gradient>"
|
||||
blocked: "&#FF6B6BС&#FF5F5Fо&#FF5353о&#FF4747б&#FF3B3Bщ&#FF2F2Fе&#FF2323н&#FF1717и&#FF0B0Bе &#FF0000з&#FF0000а&#FF0000б&#FF0000л&#FF0000о&#FF0000к&#FF0000и&#FF0000р&#FF0000о&#FF0000в&#FF0000а&#FF0000н&#FF0000о"
|
||||
warning: "&#FFA726П&#FFA322р&#FF9F1Eе&#FF9B1Aд&#FF9716у&#FF9312п&#FF8F0Eр&#FF8B0Aе&#FF8706ж&#FF8302д&#FF8000е&#FF8000н&#FF8000и&#FF8000е"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КУЛДАУНЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
cooldown:
|
||||
wait: "<gradient:#FFA726:#FF9800>Подождите {time} сек перед следующим сообщением</gradient>"
|
||||
wait: "&#FFA726П&#FFA322о&#FF9F1Eд&#FF9B1Aо&#FF9716ж&#FF9312д&#FF8F0Eи&#FF8B0Aт&#FF8706е &#FF8302{&#FF8000t&#FF8000i&#FF8000m&#FF8000e&#FF8000}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АНТИ-СПАМ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
antispam:
|
||||
caps: "<gradient:#FF6B6B:#FF4757>Слишком много заглавных букв!</gradient>"
|
||||
repeat: "<gradient:#FF6B6B:#FF4757>Не повторяйте символы!</gradient>"
|
||||
similar: "<gradient:#FF6B6B:#FF4757>Не отправляйте одинаковые сообщения!</gradient>"
|
||||
caps: "&#FF6B6BС&#FF5F5Fл&#FF5353и&#FF4747ш&#FF3B3Bк&#FF2F2Fо&#FF2323м &#FF1717м&#FF0B0Bн&#FF0000о&#FF0000г&#FF0000о &#FF0000з&#FF0000а&#FF0000г&#FF0000л&#FF0000а&#FF0000в&#FF0000н&#FF0000ы&#FF0000х"
|
||||
repeat: "&#FF6B6BН&#FF5F5Fе &#FF5353п&#FF4747о&#FF3B3Bв&#FF2F2Fт&#FF2323о&#FF1717р&#FF0B0Bя&#FF0000й&#FF0000т&#FF0000е &#FF0000с&#FF0000и&#FF0000м&#FF0000в&#FF0000о&#FF0000л&#FF0000ы"
|
||||
similar: "&#FF6B6BН&#FF5F5Fе &#FF5353о&#FF4747т&#FF3B3Bп&#FF2F2Fр&#FF2323а&#FF1717в&#FF0B0Bл&#FF0000я&#FF0000й&#FF0000т&#FF0000е &#FF0000о&#FF0000д&#FF0000и&#FF0000н&#FF0000а&#FF0000к&#FF0000о&#FF0000в&#FF0000ы&#FF0000е"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОЧИСТКА ЧАТА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
clearchat:
|
||||
cleared: "<gradient:#6BCB77:#4ECDC4>Чат очищен администратором {player}</gradient>"
|
||||
cleared: "BCB77Ч>C973аAC76Fт 3C56Bо1C367ч(C163и&BF5FщFBD5BеBB57н EB953{B74Fp�DB54BlB347aB347yB347eB347rB347}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОШИБКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
errors:
|
||||
no-permission: "<gradient:#FF6B6B:#FF4757>Нет прав!</gradient>"
|
||||
player-not-found: "<gradient:#FF6B6B:#FF4757>Игрок не найден</gradient>"
|
||||
invalid-usage: "<gradient:#FFA726:#FF9800>Использование: {usage}</gradient>"
|
||||
no-permission: "&#FF6B6BН&#FF5F5Fе&#FF5353т &#FF4747п&#FF3B3Bр&#FF2F2Fа&#FF2323в&#FF1717!"
|
||||
player-not-found: "&#FF6B6BИ&#FF5F5Fг&#FF5353р&#FF4747о&#FF3B3Bк &#FF2F2Fн&#FF2323е &#FF1717н&#FF0B0Bа&#FF0000й&#FF0000д&#FF0000е&#FF0000н"
|
||||
invalid-usage: "&#FFA726И&#FFA322с&#FF9F1Eп&#FF9B1Aо&#FF9716л&#FF9312ь&#FF8F0Eз&#FF8B0Aо&#FF8706в&#FF8302а&#FF8000н&#FF8000и&#FF8000е&#FF8000: &#FF8000{&#FF8000u&#FF8000s&#FF8000a&#FF8000g&#FF8000e&#FF8000}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СИСТЕМА МУТОВ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
mute:
|
||||
muted: "<gradient:#6BCB77:#4ECDC4>Игрок {player} замучен на {time}</gradient>"
|
||||
unmuted: "<gradient:#6BCB77:#4ECDC4>Игрок {player} размучен</gradient>"
|
||||
already-muted: "<gradient:#FFA726:#FF9800>Игрок уже замучен</gradient>"
|
||||
not-muted: "<gradient:#FFA726:#FF9800>Игрок не замучен</gradient>"
|
||||
you-muted: "<gradient:#FF6B6B:#FF4757>Вы замучены! Осталось: {time}</gradient>"
|
||||
permanent: "<gradient:#FF6B6B:#FF4757>Вы замучены навсегда!</gradient>"
|
||||
muted: "BCB77И>C973гAC76Fр3C56Bо1C367к (C163{&BF5FpFBD5BlBB57aEB953yB74Fe�DB54BrB347} B347зB347аB347мB347уB347чB347еB347н B347нB347а B347{B347tB347iB347mB347eB347}"
|
||||
unmuted: "BCB77И>C973гAC76Fр3C56Bо1C367к (C163{&BF5FpFBD5BlBB57aEB953yB74Fe�DB54BrB347} B347рB347аB347зB347мB347уB347чB347еB347н"
|
||||
already-muted: "&#FFA726И&#FFA322г&#FF9F1Eр&#FF9B1Aо&#FF9716к &#FF9312у&#FF8F0Eж&#FF8B0Aе &#FF8706з&#FF8302а&#FF8000м&#FF8000у&#FF8000ч&#FF8000е&#FF8000н"
|
||||
not-muted: "&#FFA726И&#FFA322г&#FF9F1Eр&#FF9B1Aо&#FF9716к &#FF9312н&#FF8F0Eе &#FF8B0Aз&#FF8706а&#FF8302м&#FF8000у&#FF8000ч&#FF8000е&#FF8000н"
|
||||
you-muted: "&#FF6B6BВ&#FF5F5Fы &#FF5353з&#FF4747а&#FF3B3Bм&#FF2F2Fу&#FF2323ч&#FF1717е&#FF0B0Bн&#FF0000ы&#FF0000! &#FF0000О&#FF0000с&#FF0000т&#FF0000а&#FF0000л&#FF0000о&#FF0000с&#FF0000ь&#FF0000: &#FF0000{&#FF0000t&#FF0000i&#FF0000m&#FF0000e&#FF0000}"
|
||||
permanent: "&#FF6B6BВ&#FF5F5Fы &#FF5353з&#FF4747а&#FF3B3Bм&#FF2F2Fу&#FF2323ч&#FF1717е&#FF0B0Bн&#FF0000ы &#FF0000н&#FF0000а&#FF0000в&#FF0000с&#FF0000е&#FF0000г&#FF0000д&#FF0000а&#FF0000!"
|
||||
|
|
@ -241,3 +241,18 @@ permissions:
|
|||
lochat.nick:
|
||||
description: Can use custom nicknames
|
||||
default: true
|
||||
lochat.mention.everyone:
|
||||
description: Can mention @everyone
|
||||
default: op
|
||||
lochat.mention.here:
|
||||
description: Can mention @here
|
||||
default: op
|
||||
lochat.mention.role:
|
||||
description: Can mention @role:<permission>
|
||||
default: op
|
||||
lochat.bypass.urlfilter:
|
||||
description: Bypass URL filter
|
||||
default: op
|
||||
lochat.bypass.ipfilter:
|
||||
description: Bypass IP filter
|
||||
default: op
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue