9 Commits
Author SHA1 Message Date
auto 3b2aa84164 Add player muted logging and abstract embed building
The update introduces a log entry indicating when a player has been muted due to voting. The embed creation for this process has been isolated and extracted into a separate function. This contributes to better code modularity and organization.
2024-04-13 16:52:56 +02:00
auto 0fb497c2f1 Merge branch 'main' into vote_mute 2024-04-13 16:15:56 +02:00
auto 192fca3a89 Fix chat log message deletion query
Corrected the SQL query in the `deleteOldMessages` method within `ChatLogQueries.java`. Originally, it was incorrectly deleting newer messages rather than older ones due to an incorrect comparison symbol. It has now been adjusted to properly delete older messages based on the provided duration.
2024-04-07 19:16:32 +02:00
auto d6135f2456 Add mute vote results sent to Discord and staff presence check
Enhanced the vote-to-mute feature by adding a function that sends the mute vote results to a general channel on Discord. A 'staff presence' check has also been added which prevents the mute vote from being initiated if a staff member is online, instead, it prompts users to directly contact a staff member for help.
2024-04-07 18:29:46 +02:00
auto c057c69653 Enable optional logging in ChatLogHandler
Modified the ChatLogHandler to support optional logging by introducing a new argument in the getInstance() method. This argument sets the logging state during instantiation. This facilitates better flexibility when using the ChatLogHandler across different sections of the code base as logging requirements may differ.
2024-04-07 18:11:19 +02:00
auto c25767e473 Add VoteToMuteHelper and enhance ActiveVoteToMute
Implemented a new module titled VoteToMuteHelper to enhance the voting system and augment the user experience. This module enhances the system by providing relevant player suggestions and setting up the mute player. Made updates to the ActiveVoteToMute module to handle potential voters and mute the player if the vote is passed. VoteToMute module is also updated to include the total eligible players. The code is made robust by adding appropriate error checks.
2024-04-07 18:07:20 +02:00
auto 37aa9fdf4c Replace Object2ObjectOpenHashMap with HashMap
The usage of Object2ObjectOpenHashMap in storing chat logs and the chat log handler was switched to HashMap. This was done to ensure the plugin can be run on proxy as velocity does not include this library
2024-04-07 16:32:33 +02:00
auto ed2ba74772 Add start of VoteToMute functionality in chat system
Implemented VoteToMute system enabling initiated voting for muting a player in chat. This includes creating new classes "ActiveVoteToMute", "VoteToMute", and "VoteToMuteStarter". The "VoteToMute" class handles the voting command logic, it allows players to vote on whether to mute other players. The code also adds a call to register this new command in the main VelocityChat class.
2024-04-06 19:39:36 +02:00
auto 44d6e994cc Implement chat log handler with database support
The code changes introduce the ability to log chat messages. A new ChatLogHandler class has been added that manages the queue of chat log messages, both storing them in memory and writing them to a database. New columns have been added to the database and the interactivity with the database is handled using prepared statements to improve security and performance. The chat messages are deleted from the database after a certain period, which can be configured.
2024-04-06 17:25:15 +02:00
25 changed files with 179 additions and 275 deletions
Vendored
-20
View File
@@ -1,20 +0,0 @@
pipeline {
agent any
stages {
stage('Gradle') {
steps {
sh './gradlew build'
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'build/libs/', followSymlinks: false
}
}
stage('discord') {
steps {
discordSend description: "Build: ${BUILD_NUMBER}", showChangeset: true, result: currentBuild.currentResult, title: currentBuild.fullProjectName, webhookURL: env.discordwebhook
}
}
}
}
+2 -3
View File
@@ -3,10 +3,9 @@ plugins {
}
dependencies {
// compileOnly("com.alttd:Galaxy-API:1.21-R0.1-SNAPSHOT") {
compileOnly("com.alttd:Galaxy-API:1.19.2-R0.1-SNAPSHOT") {
// exclude("net.kyori")
// }
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
}
compileOnly("org.spongepowered:configurate-yaml:4.1.2") // Configurate
compileOnly("net.luckperms:api:5.3") // Luckperms
}
@@ -32,7 +32,7 @@ public final class Config {
public static File CONFIGPATH;
public static void init() {
CONFIGPATH = new File(File.separator + "mnt" + File.separator + "configs" + File.separator + "ChatPlugin");
CONFIGPATH = new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "ChatPlugin");
CONFIG_FILE = new File(CONFIGPATH, "config.yml");
configLoader = YamlConfigurationLoader.builder()
.file(CONFIG_FILE)
@@ -28,10 +28,8 @@ public class ChatLogHandler {
private final HashMap<UUID, List<ChatLog>> chatLogs = new HashMap<>();
public ChatLogHandler(boolean enableLogging) {
if (!enableLogging) {
ALogger.info("Logging is not enabled on this server.");
if (!enableLogging)
return;
}
Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS);
ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> {
if (success) {
@@ -41,12 +39,8 @@ public class ChatLogHandler {
}
});
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() -> {
saveToDatabase(false);
ALogger.info(String.format("Running scheduler to save messages with a %d delay", Config.CHAT_LOG_SAVE_DELAY_MINUTES));
},
executorService.scheduleAtFixedRate(() -> saveToDatabase(false),
Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES);
ALogger.info("Logging has started!");
}
/**
@@ -76,27 +70,19 @@ public class ChatLogHandler {
private void saveToDatabase(boolean onMainThread) {
savingToDatabase(true);
ALogger.info(String.format("Saving %d messages to database", chatLogs.size()));
CompletableFuture<Boolean> booleanCompletableFuture = ChatLogQueries.storeMessages(chatLogs);
if (onMainThread) {
booleanCompletableFuture.join();
ALogger.info("Finished saving messages on main thread");
return;
}
booleanCompletableFuture.whenComplete((result, throwable) -> {
if (throwable == null && result) {
chatLogs.clear();
} else {
ALogger.error("Failed to save chat messages.");
}
savingToDatabase(false);
if (!chatLogQueue.isEmpty()) {
ALogger.info("Adding back messages from queue to chatLogs map");
}
while (!chatLogQueue.isEmpty()) {
addLog(chatLogQueue.remove());
}
ALogger.info("Finished saving messages");
});
}
+15 -2
View File
@@ -1,12 +1,25 @@
plugins {
`java-library`
id("io.github.goooler.shadow") version "8.1.8"
id("com.github.johnrengelman.shadow") version "7.1.0"
}
allprojects {
group = "com.alttd.chat"
version = "2.0.0-SNAPSHOT"
description = "All in one minecraft chat plugin"
// repositories {
// mavenCentral()
// maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy
// maven("https://oss.sonatype.org/content/groups/public/") // Adventure
// maven("https://oss.sonatype.org/content/repositories/snapshots/") // Minimessage
// maven("https://oss.sonatype.org/content/repositories/") // Minimessage
// maven("https://nexus.velocitypowered.com/repository/") // Velocity
// maven("https://nexus.velocitypowered.com/repository/maven-public/") // Velocity
// maven("https://repo.spongepowered.org/maven") // Configurate
// maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // Papi
// maven("https://jitpack.io")
// }
}
subprojects {
@@ -14,7 +27,7 @@ subprojects {
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
languageVersion.set(JavaLanguageVersion.of(17))
}
}
+36 -3
View File
@@ -1,25 +1,58 @@
import java.io.FileOutputStream
import java.net.URL
plugins {
`maven-publish`
id("io.github.goooler.shadow")
id("com.github.johnrengelman.shadow")
id("xyz.jpenilla.run-paper") version "1.0.6"
}
dependencies {
implementation(project(":api")) // API
// compileOnly("com.alttd:Galaxy-API:1.21-R0.1-SNAPSHOT") // Galaxy
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
compileOnly("com.alttd:Galaxy-API:1.20.4-R0.1-SNAPSHOT") // Galaxy
compileOnly("com.gitlab.ruany:LiteBansAPI:0.3.5") // move to proxy
compileOnly("org.apache.commons:commons-lang3:3.12.0") // needs an alternative, already removed from upstream api and will be removed in server
compileOnly("net.luckperms:api:5.3") // Luckperms
compileOnly(files("../libs/CMI.jar"))
}
tasks {
shadowJar {
archiveFileName.set("${rootProject.name}-${project.name}-${project.version}.jar")
// minimize()
}
build {
// setBuildDir("${rootProject.buildDir}")
dependsOn(shadowJar)
}
runServer {
val dir = File(System.getProperty("user.home") + "/share/devserver/");
if (!dir.parentFile.exists()) {
dir.parentFile.mkdirs()
}
runDirectory.set(dir)
val fileName = "/galaxy.jar"
var file = File(dir.path + fileName)
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
}
if (!file.exists()) {
download("https://repo.destro.xyz/snapshots/com/alttd/Galaxy-Server/Galaxy-paperclip-1.19.2-R0.1-SNAPSHOT-reobf.jar", file)
}
serverJar(file)
minecraftVersion("1.19.2")
}
}
fun download(link: String, path: File) {
URL(link).openStream().use { input ->
FileOutputStream(path).use { output ->
input.copyTo(output)
}
}
}
@@ -38,9 +38,9 @@ public class ChatPlugin extends JavaPlugin {
chatAPI = new ChatImplementation();
chatHandler = new ChatHandler();
DatabaseConnection.initialize();
serverConfig = new ServerConfig(Bukkit.getServer().getName());
serverConfig = new ServerConfig(Bukkit.getServerName());
ChatLogHandler chatLogHandler = ChatLogHandler.getInstance(true);
registerListener(new PlayerListener(serverConfig), new ChatListener(chatLogHandler), new BookListener(), new ShutdownListener(chatLogHandler, this));
registerListener(new PlayerListener(serverConfig), new ChatListener(chatLogHandler), new BookListener(), new ShutdownListener(chatLogHandler));
if(serverConfig.GLOBALCHAT) {
registerCommand("globalchat", new GlobalChat());
registerCommand("toggleglobalchat", new ToggleGlobalChat());
@@ -119,7 +119,7 @@ public class ChatPlugin extends JavaPlugin {
public void ReloadConfig() {
chatAPI.ReloadConfig();
chatAPI.ReloadChatFilters();
serverConfig = new ServerConfig(Bukkit.getServer().getName());
serverConfig = new ServerConfig(Bukkit.getServerName());
Bukkit.broadcast(Utility.parseMiniMessage("Reloaded ChatPlugin Config."), "command.chat.reloadchat");
ALogger.info("Reloaded ChatPlugin config.");
}
@@ -38,7 +38,7 @@ public class ChatChannel extends BukkitCommand {
}
if(args.length == 0 && player.hasPermission(channel.getPermission())) {
player.sendRichMessage(Config.CUSTOM_CHANNEL_TOGGLED, TagResolver.resolver(
player.sendMiniMessage(Config.CUSTOM_CHANNEL_TOGGLED, TagResolver.resolver(
Placeholder.unparsed("channel", channel.getChannelName()),
Placeholder.component("status", toggleableForCustomChannel.toggle(player.getUniqueId())
? Config.TOGGLED_ON : Config.TOGGLED_OFF)));
@@ -27,7 +27,7 @@ public class PartyChat extends Toggleable implements CommandExecutor {
}
if(args.length == 0) {
player.sendRichMessage(Config.PARTY_TOGGLED, Placeholder.component("status",
player.sendMiniMessage(Config.PARTY_TOGGLED, Placeholder.component("status",
toggle(player.getUniqueId()) ? Config.TOGGLED_ON : Config.TOGGLED_OFF));
return true;
}
@@ -26,7 +26,6 @@ import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public class ChatHandler {
@@ -131,7 +130,7 @@ public class ChatHandler {
Placeholder.component("sender", senderName),
Placeholder.component("prefix", prefix),
Placeholder.component("message", parseMessageContent(player, message)),
Placeholder.parsed("server", Bukkit.getServer().getName())
Placeholder.parsed("server", Bukkit.getServerName())
);
Component component = Utility.parseMiniMessage(Config.GCFORMAT, placeholders);
@@ -165,7 +164,7 @@ public class ChatHandler {
TagResolver placeholders = TagResolver.resolver(
Placeholder.component("sender", senderName),
Placeholder.component("message", parseMessageContent(player, message)),
Placeholder.parsed("server", Bukkit.getServer().getName()),
Placeholder.parsed("server", Bukkit.getServerName()),
Placeholder.parsed("channel", channel.getChannelName())
);
Component component = Utility.parseMiniMessage(channel.getFormat(), placeholders);
@@ -238,21 +237,12 @@ public class ChatHandler {
}
private void sendChatChannelMessage(CustomChannel chatChannel, UUID uuid, Component component) {
if (!chatChannel.getServers().contains(Bukkit.getServer().getName())) {
return;
}
if (!chatChannel.getServers().contains(Bukkit.getServerName())) return;
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
Stream<? extends Player> stream = Bukkit.getServer().getOnlinePlayers().stream()
.filter(p -> p.hasPermission(chatChannel.getPermission()));
if (!player.hasPermission("chat.ignorebypass")) {
stream = stream.filter(p -> !ChatUserManager.getChatUser(p.getUniqueId()).getIgnoredPlayers().contains(uuid) && !p.hasPermission("chat.ignorebypass"));
}
stream.forEach(p -> p.sendMessage(component));
Bukkit.getServer().getOnlinePlayers().stream()
.filter(p -> p.hasPermission(chatChannel.getPermission()))
.filter(p -> !ChatUserManager.getChatUser(p.getUniqueId()).getIgnoredPlayers().contains(uuid))
.forEach(p -> p.sendMessage(component));
}
private void sendPluginMessage(Player player, String channel, Component component) {
@@ -32,10 +32,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ChatListener implements Listener {
@@ -83,14 +81,18 @@ public class ChatListener implements Listener {
}
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
Set<Player> receivers = event.viewers().stream().filter(audience -> audience instanceof Player)
.map(audience -> (Player) audience)
.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()))
.collect(Collectors.toSet());
Component input = event.message().colorIfAbsent(NamedTextColor.WHITE);
ModifiableString modifiableString = new ModifiableString(input);
// todo a better way for this
if(!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> {
if(!RegexManager.filterText(player.getName(), player.getUniqueId(), modifiableString, true, "chat", filterType -> {
if (!filterType.equals(FilterType.PUNISH)) {
ALogger.warn("Received another FilterType than punish when filtering chat and executing a filter action");
return;
@@ -98,7 +100,7 @@ public class ChatListener implements Listener {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("punish");
out.writeUTF(player.getName());
out.writeUTF(uuid.toString());
out.writeUTF(player.getUniqueId().toString());
out.writeUTF(modifiableString.string());
player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray());
})) {
@@ -106,19 +108,10 @@ public class ChatListener implements Listener {
GalaxyUtility.sendBlockedNotification("Language", player,
modifiableString.component(),
"");
chatLogHandler.addChatLog(uuid, player.getServer().getName(), PlainTextComponentSerializer.plainText().serialize(input), true);
chatLogHandler.addChatLog(player.getUniqueId(), player.getServer().getServerName(), PlainTextComponentSerializer.plainText().serialize(input), true);
return; // the message was blocked
}
Stream<Player> stream = event.viewers().stream().filter(audience -> audience instanceof Player)
.map(audience -> (Player) audience);
if (!player.hasPermission("chat.ignorebypass")) {
stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains(uuid)
&& !receiver.hasPermission("chat.ignorebypass"));
}
Set<Player> receivers = stream.collect(Collectors.toSet());
Set<Player> playersToPing = new HashSet<>();
pingPlayers(playersToPing, modifiableString, player);
@@ -129,7 +122,7 @@ public class ChatListener implements Listener {
for (Player pingPlayer : playersToPing) {
pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1);
}
chatLogHandler.addChatLog(uuid, player.getServer().getName(), modifiableString.string(), false);
chatLogHandler.addChatLog(player.getUniqueId(), player.getServer().getServerName(), modifiableString.string(), false);
ALogger.info(PlainTextComponentSerializer.plainText().serialize(input));
}
@@ -143,12 +136,11 @@ public class ChatListener implements Listener {
Pattern nickPattern = Pattern.compile("\\b(?<!\\\\)" + nickName + "\\b", Pattern.CASE_INSENSITIVE);
// Pattern escapedNickPattern = Pattern.compile("\\b\\\\" + nickName + "\\b", Pattern.CASE_INSENSITIVE);
ChatUser onlinePlayerUser = ChatUserManager.getChatUser(onlinePlayer.getUniqueId());
if (namePattern.matcher(modifiableString.string()).find()) {
modifiableString.replace(TextReplacementConfig.builder()
.once()
.match(namePattern)
.replacement(mention.append(onlinePlayerUser.getDisplayName()))
.replacement(mention.append(onlinePlayer.displayName()))
.build());
//TODO replace all instances of \name with just name but using the match result so the capitalization doesn't change
// modifiableString.replace(TextReplacementConfig.builder()
@@ -158,17 +150,15 @@ public class ChatListener implements Listener {
// String substring = a.group().substring(1);
// return ;
// });
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId())
&& !player.hasPermission("chat.ignorebypass"))
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()))
playersToPing.add(onlinePlayer);
} else if (nickPattern.matcher(modifiableString.string()).find()) {
modifiableString.replace(TextReplacementConfig.builder()
.once()
.match(nickPattern)
.replacement(mention.append(onlinePlayerUser.getDisplayName()))
.replacement(mention.append(onlinePlayer.displayName()))
.build());
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId())
&& !player.hasPermission("chat.ignorebypass"))
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()))
playersToPing.add(onlinePlayer);
}
}
@@ -11,13 +11,11 @@ import com.alttd.chat.objects.Toggleable;
import com.alttd.chat.util.GalaxyUtility;
import com.alttd.chat.util.Utility;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -50,8 +48,8 @@ public class PlayerListener implements Listener {
UUID uuid = player.getUniqueId();
Toggleable.disableToggles(uuid);
if (serverConfig.FIRST_JOIN_MESSAGES && (!player.hasPlayedBefore() || System.currentTimeMillis() - player.getFirstPlayed() < TimeUnit.SECONDS.toMillis(10))) {
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Config.FIRST_JOIN, Placeholder.parsed("player", player.getName())));
if (serverConfig.FIRST_JOIN_MESSAGES && System.currentTimeMillis() - player.getFirstPlayed() < TimeUnit.SECONDS.toMillis(10)) {
player.getServer().sendMessage(MiniMessage.miniMessage().deserialize(Config.FIRST_JOIN, Placeholder.parsed("player", player.getName())));
}
ChatUser user = ChatUserManager.getChatUser(uuid);
@@ -105,36 +103,22 @@ public class PlayerListener implements Listener {
Stack<Instant> playerDeathsStack = sendPlayerDeaths.computeIfAbsent(uuid, key -> new Stack<>());
Instant cutOff = Instant.now().minus(Config.DEATH_MESSAGES_LIMIT_PERIOD_MINUTES, ChronoUnit.MINUTES);
while (!playerDeathsStack.isEmpty() && playerDeathsStack.peek().isBefore(cutOff)) {
while (playerDeathsStack.peek().isBefore(cutOff)) {
playerDeathsStack.pop();
}
if (playerDeathsStack.size() > Config.DEATH_MESSAGES_MAX_PER_PERIOD || serverConfig.MUTED) {
event.deathMessage(Component.empty());
return;
}
} else {
Component component = event.deathMessage();
if (component != null) {
component = Component.text("* ").append(component);
component = component.style(Style.style(TextColor.color(82, 80, 77), TextDecoration.ITALIC));
event.deathMessage(component);
}
}
playerDeathsStack.push(Instant.now());
if (component == null) {
return;
}
TextReplacementConfig playerReplacement = TextReplacementConfig.builder()
.match(event.getPlayer().getName())
.replacement(event.getPlayer().displayName())
.build();
component = component.replaceText(playerReplacement);
Player killer = event.getPlayer().getKiller();
if (killer != null) {
TextReplacementConfig killerReplacement = TextReplacementConfig.builder()
.match(killer.getName())
.replacement(killer.displayName())
.build();
component = component.replaceText(killerReplacement);
}
component = MiniMessage.miniMessage().deserialize("<dark_red>[</dark_red><red>☠</red><dark_red>]</dark_red> ").append(component);
component = component.style(Style.style(TextColor.color(255, 155, 48), TextDecoration.ITALIC));
event.deathMessage(component);
}
}
@@ -15,20 +15,20 @@ import com.alttd.chat.util.Utility;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public class PluginMessage implements PluginMessageListener {
@Override
public void onPluginMessageReceived(String channel, @NotNull Player ignored, byte[] bytes) {
public void onPluginMessageReceived(String channel, Player player, byte[] bytes) {
if (!channel.equals(Config.MESSAGECHANNEL)) {
return;
}
@@ -38,36 +38,35 @@ public class PluginMessage implements PluginMessageListener {
case "privatemessagein": {
UUID uuid = UUID.fromString(in.readUTF());
String target = in.readUTF();
Player player = Bukkit.getPlayer(uuid);
Player p = Bukkit.getPlayer(uuid);
String message = in.readUTF();
UUID targetuuid = UUID.fromString(in.readUTF());
if (player == null) {
break;
}
ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (isTargetNotIgnored(chatUser, targetuuid)) {
player.sendMessage(GsonComponentSerializer.gson().deserialize(message));
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); // todo load this from config
ChatUser user = ChatUserManager.getChatUser(uuid);
if (!user.getReplyContinueTarget().equalsIgnoreCase(target))
user.setReplyTarget(target);
if (p != null) {
ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (!chatUser.getIgnoredPlayers().contains(targetuuid)) {
p.sendMessage(GsonComponentSerializer.gson().deserialize(message));
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); // todo load this from config
ChatUser user = ChatUserManager.getChatUser(uuid);
if (!user.getReplyContinueTarget().equalsIgnoreCase(target))
user.setReplyTarget(target);
}
}
break;
}
case "privatemessageout": {
UUID uuid = UUID.fromString(in.readUTF());
String target = in.readUTF();
Player player = Bukkit.getPlayer(uuid);
Player p = Bukkit.getPlayer(uuid);
String message = in.readUTF();
UUID targetuuid = UUID.fromString(in.readUTF());
if (player == null) {
break;
}
ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (isTargetNotIgnored(chatUser, targetuuid)) {
chatUser.setReplyTarget(target);
player.sendMessage(GsonComponentSerializer.gson().deserialize(message));
if (p != null) {
ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (!chatUser.getIgnoredPlayers().contains(targetuuid)) {
chatUser.setReplyTarget(target);
p.sendMessage(GsonComponentSerializer.gson().deserialize(message));
// ChatUser user = ChatUserManager.getChatUser(uuid);
// user.setReplyTarget(target);
}
}
break;
}
@@ -79,7 +78,7 @@ public class PluginMessage implements PluginMessageListener {
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission(Config.GCPERMISSION)).forEach(p -> {
ChatUser chatUser = ChatUserManager.getChatUser(p.getUniqueId());
if (isTargetNotIgnored(chatUser, uuid)) {
if (!chatUser.getIgnoredPlayers().contains(uuid)) {
p.sendMessage(GsonComponentSerializer.gson().deserialize(message));
}
});
@@ -194,7 +193,7 @@ public class PluginMessage implements PluginMessageListener {
ALogger.warn("Received ChatChannel message for non existent channel.");
return;
}
if (!chatChannel.getServers().contains(Bukkit.getServer().getName())) {
if (!chatChannel.getServers().contains(Bukkit.getServerName())) {
ALogger.warn("Received ChatChannel message for the wrong server.");
return;
}
@@ -217,15 +216,4 @@ public class PluginMessage implements PluginMessageListener {
}.runTaskAsynchronously(ChatPlugin.getInstance());
}
private boolean isTargetNotIgnored(ChatUser chatUser, UUID targetUUID) {
if (!chatUser.getIgnoredPlayers().contains(targetUUID)) {
return true;
}
Player target = Bukkit.getPlayer(targetUUID);
if (target == null) {
return true;
}
return target.hasPermission("chat.ignorebypass");
}
}
@@ -4,23 +4,17 @@ import com.alttd.chat.objects.chat_log.ChatLogHandler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
public class ShutdownListener implements Listener {
private final ChatLogHandler chatLogHandler;
private final Plugin thisPlugin;
public ShutdownListener(ChatLogHandler chatLogHandler, Plugin thisPlugin) {
public ShutdownListener(ChatLogHandler chatLogHandler) {
this.chatLogHandler = chatLogHandler;
this.thisPlugin = thisPlugin;
}
@EventHandler
public void onShutdown(PluginDisableEvent event) {
if (!event.getPlugin().getName().equals(thisPlugin.getName())){
return;
}
chatLogHandler.shutDown();
}
@@ -149,24 +149,24 @@ public class NickUtilities
public static boolean validNick(Player sender, OfflinePlayer target, String nickName) {
if (!noBlockedCodes(nickName)) {
sender.sendRichMessage(Config.NICK_BLOCKED_COLOR_CODES);
sender.sendMiniMessage(Config.NICK_BLOCKED_COLOR_CODES, null);
return false;
}
if (!Utility.checkNickBrightEnough(nickName)) {
sender.sendRichMessage("<red>At least one color must be brighter than 30 for each color</red>");
sender.sendMiniMessage("<red>At least one color must be brighter than 30 for each color</red>", null);
return false;
}
String cleanNick = NickUtilities.removeAllColors(nickName);
if (cleanNick.length() < 3 || cleanNick.length() > 16) {
sender.sendRichMessage(Config.NICK_INVALID_LENGTH);
sender.sendMiniMessage(Config.NICK_INVALID_LENGTH, null);
return false;
}
if (!cleanNick.matches("[a-zA-Z0-9_]*") || nickName.length() > 192) { //192 is if someone puts {#xxxxxx<>} in front of every letter
sender.sendRichMessage(Config.NICK_INVALID_CHARACTERS);
sender.sendMiniMessage(Config.NICK_INVALID_CHARACTERS, null);
return false;
}
@@ -183,7 +183,7 @@ public class NickUtilities
if (uniqueId.equals(uuid)){
ChatPlugin.getInstance().getLogger().info(uuid + " " + uniqueId);
}
sender.sendRichMessage(Config.NICK_TAKEN);
sender.sendMiniMessage(Config.NICK_TAKEN, null);
return false;
}
}
@@ -1,5 +1,7 @@
package com.alttd.chat.nicknames;
import com.Zrips.CMI.CMI;
import com.Zrips.CMI.Containers.CMIUser;
import com.alttd.chat.ChatAPI;
import com.alttd.chat.ChatPlugin;
import com.alttd.chat.config.Config;
@@ -11,6 +13,7 @@ import com.alttd.chat.objects.Nick;
import com.alttd.chat.util.Utility;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.luckperms.api.LuckPerms;
@@ -111,7 +114,7 @@ public class Nicknames implements CommandExecutor, TabCompleter {
Placeholder.component("nickname", chatUser.getDisplayName()),
Placeholder.parsed("currentnickname", chatUser.getNickNameString())
);
player.sendRichMessage(Config.NICK_CURRENT, placeholders);
player.sendMiniMessage(Config.NICK_CURRENT, placeholders);
}
break;
case "help":
@@ -381,7 +384,7 @@ public class Nicknames implements CommandExecutor, TabCompleter {
ChatUser user = ChatUserManager.getChatUser(player.getUniqueId());
user.setDisplayName(player.getName());
player.displayName(user.getDisplayName());
// updateCMIUser(player, null);
updateCMIUser(player, null);
}
public String getNick(final Player player) {
@@ -396,25 +399,25 @@ public class Nicknames implements CommandExecutor, TabCompleter {
ChatUser user = ChatUserManager.getChatUser(player.getUniqueId());
user.setDisplayName(nickName);
player.displayName(user.getDisplayName());
// updateCMIUser(player, nickName);
updateCMIUser(player, nickName);
}
// public static String format(final String m) {
// return NickUtilities.applyColor(m);
// }
// public void updateCMIUser(Player player, String nickName) {
// if (!isCMIEnabled())
// return;
//
// CMIUser cmiUser = CMI.getInstance().getPlayerManager().getUser(player);
// if (nickName == null){
// cmiUser.setNickName(null, true);
// } else {
// cmiUser.setNickName(NickUtilities.applyColor(nickName), true);
// }
// cmiUser.updateDisplayName();
// }
public void updateCMIUser(Player player, String nickName) {
if (!isCMIEnabled())
return;
CMIUser cmiUser = CMI.getInstance().getPlayerManager().getUser(player);
if (nickName == null){
cmiUser.setNickName(null, true);
} else {
cmiUser.setNickName(NickUtilities.applyColor(nickName), true);
}
cmiUser.updateDisplayName();
}
private Boolean isCMIEnabled = null;
private Boolean isCMIEnabled() {
@@ -1,8 +1,12 @@
package com.alttd.chat.nicknames;
import com.Zrips.CMI.commands.list.colorlimits;
import com.Zrips.CMI.utils.Util;
import com.alttd.chat.ChatPlugin;
import com.alttd.chat.config.Config;
import com.alttd.chat.database.Queries;
import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.objects.ChatUser;
import com.alttd.chat.objects.Nick;
import com.alttd.chat.util.ALogger;
import com.google.common.io.ByteArrayDataInput;
@@ -41,7 +41,6 @@ public class ToggleableForCustomChannel extends Toggleable {
new BukkitRunnable() {
@Override
public void run() {
ALogger.info(String.format("%s sent %s message: %s", player.getName(), customChannel.getChannelName(), message));
ChatPlugin.getInstance().getChatHandler().chatChannel(player, customChannel, message);
}
}.runTaskAsynchronously(ChatPlugin.getInstance());
Executable
BIN
View File
Binary file not shown.
+1 -12
View File
@@ -4,14 +4,11 @@ include(":api")
include(":galaxy")
include(":velocity")
val nexusUser = providers.gradleProperty("alttdSnapshotUsername").get()
val nexusPass = providers.gradleProperty("alttdSnapshotPassword").get()
dependencyResolutionManagement {
repositories {
// mavenLocal()
mavenCentral()
// maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy
maven("https://repo.destro.xyz/snapshots") // Altitude - Galaxy
maven("https://oss.sonatype.org/content/groups/public/") // Adventure
maven("https://oss.sonatype.org/content/repositories/snapshots/") // Minimessage
maven("https://nexus.velocitypowered.com/repository/") // Velocity
@@ -19,14 +16,6 @@ dependencyResolutionManagement {
maven("https://repo.spongepowered.org/maven") // Configurate
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // Papi
maven("https://jitpack.io")
maven {
name = "nexus"
url = uri("https://repo.alttd.com/repository/alttd-snapshot/")
credentials {
username = nexusUser
password = nexusPass
}
}
}
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
+2 -2
View File
@@ -1,6 +1,6 @@
plugins {
`maven-publish`
id("io.github.goooler.shadow")
id("com.github.johnrengelman.shadow")
}
dependencies {
@@ -11,7 +11,7 @@ dependencies {
implementation("org.spongepowered", "configurate-yaml", "4.1.2")
compileOnly("net.kyori:adventure-text-minimessage:4.10.1")
compileOnly("com.gitlab.ruany:LiteBansAPI:0.3.5")
compileOnly("com.alttd.proxydiscordlink:ProxyDiscordLink:1.0.1-SNAPSHOT")
compileOnly("com.alttd.proxydiscordlink:ProxyDiscordLink:1.0.0-BETA-SNAPSHOT")
}
tasks {
@@ -110,10 +110,10 @@ public class VoteToMute {
}
boolean countLowerRanks = false;
long count = getTotalEligiblePlayers(server, false);
if (count < 6) {
if (count < 10) {
countLowerRanks = true;
count = getTotalEligiblePlayers(server, true);
if (count < 6) {
if (count < 10) {
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Not enough eligible players online to vote.</red>"));
return 1;
}
@@ -16,7 +16,6 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import jdk.jshell.execution.Util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
@@ -30,8 +29,6 @@ import java.util.stream.IntStream;
public class VoteToMuteHelper {
private static final Component prefix = Utility.parseMiniMessage("<gold>[VoteMute]</gold>");
public VoteToMuteHelper(ProxyServer proxyServer) {
RequiredArgumentBuilder<CommandSource, String> playerNode = RequiredArgumentBuilder
.<CommandSource, String>argument("player", StringArgumentType.string())
@@ -119,7 +116,7 @@ public class VoteToMuteHelper {
return 1;
});
LiteralArgumentBuilder<CommandSource> enterMessagesNode = LiteralArgumentBuilder
LiteralArgumentBuilder<CommandSource> enterPageNode = LiteralArgumentBuilder
.<CommandSource>literal("messages")
.requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute"))
.then(RequiredArgumentBuilder.<CommandSource, String>argument("list of messages", StringArgumentType.greedyString())
@@ -150,7 +147,7 @@ public class VoteToMuteHelper {
}
int highestLogEntry = max.get();
if (voteToMuteStarter.getTotalLogEntries() < highestLogEntry) {
if (voteToMuteStarter.getTotalLogEntries() > highestLogEntry) {
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Some of your selected messages do not exist.</red>"));
return 1;
}
@@ -165,7 +162,7 @@ public class VoteToMuteHelper {
RegisteredServer server = currentServer.get().getServer();
long count = getTotalEligiblePlayers(server, voteToMuteStarter.countLowerRanks());
new ActiveVoteToMute(voteToMuteStarter.getVotedPlayer(), server, proxyServer, Duration.ofMinutes(5),
(int) count, voteToMuteStarter.countLowerRanks(), chatLogs, player)
(int) count, voteToMuteStarter.countLowerRanks(), chatLogs)
.start();
return 1;
})
@@ -179,11 +176,11 @@ public class VoteToMuteHelper {
.then(playerNode
.then(yesNoNode
.executes(commandContext -> {
if (!(commandContext.getSource() instanceof Player player)) {
if (!(commandContext.getSource() instanceof Player)) {
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
"<red>Only players are allowed to vote</red>"));
return 1;
}
Player source = (Player) commandContext.getSource();
String playerName = commandContext.getArgument("player", String.class);
Optional<ActiveVoteToMute> optionalActiveVoteToMute = ActiveVoteToMute.getInstance(playerName);
if (optionalActiveVoteToMute.isEmpty()) {
@@ -194,25 +191,16 @@ public class VoteToMuteHelper {
ActiveVoteToMute activeVoteToMute = optionalActiveVoteToMute.get();
if (!activeVoteToMute.countLowerRanks()) {
if (!player.hasPermission("chat.vote-to-mute")) {
player.sendMessage(Utility.parseMiniMessage("<red>You are not eligible to vote.</red>"));
if (!source.hasPermission("chat.vote-to-mute")) {
source.sendMessage(Utility.parseMiniMessage("<red>You are not eligible to vote.</red>"));
return 1;
}
}
String vote = commandContext.getArgument("yesNo", String.class);
String vote = commandContext.getArgument("yesno", String.class);
switch (vote.toLowerCase()) {
case "yes" -> {
activeVoteToMute.vote(player.getUniqueId(), true);
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
"<green>You voted to mute. Thanks for voting, staff will be online soon to review!</green>"));
player.getCurrentServer().ifPresent(serverConnection -> notifyEligiblePlayers(serverConnection.getServer(), activeVoteToMute));
}
case "no" -> {
activeVoteToMute.vote(player.getUniqueId(), false);
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
"<green>You voted <red>not</red> to mute. Thanks for voting, staff will be online soon to review!</green>"));
}
case "yes" -> activeVoteToMute.vote(source.getUniqueId(), true);
case "no" -> activeVoteToMute.vote(source.getUniqueId(), false);
default -> commandContext.getSource().sendMessage(Utility.parseMiniMessage(
"<red><vote> is not a valid vote option</red>", Placeholder.parsed("vote", vote)));
}
@@ -231,7 +219,7 @@ public class VoteToMuteHelper {
.requires(commandSource -> commandSource instanceof Player)
.then(voteNode)
.then(pageNode)
.then(enterMessagesNode)
.then(enterPageNode)
.executes(context -> {
sendHelpMessage(context.getSource());
return 1;
@@ -253,18 +241,6 @@ public class VoteToMuteHelper {
.count();
}
private void notifyEligiblePlayers(RegisteredServer server, ActiveVoteToMute activeVoteToMute) {
Component message = Utility.parseMiniMessage("<prefix><green><voted_for> out of <total_votes> players have voted to mute <player></green>",
Placeholder.component("prefix", prefix),
Placeholder.parsed("voted_for", String.valueOf(activeVoteToMute.getVotedFor())),
Placeholder.parsed("total_votes", String.valueOf(activeVoteToMute.getTotalEligibleVoters())),
Placeholder.parsed("player", activeVoteToMute.getVotedPlayer().getUsername()));
boolean countLowerRanks = activeVoteToMute.countLowerRanks();
server.getPlayersConnected().stream()
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
.forEach(player -> player.sendMessage(message));
}
private void sendHelpMessage(CommandSource commandSource) {
commandSource.sendMessage(Utility.parseMiniMessage("<red>Use: <gold>/votetomutehelper <player></gold>.</red>"));
}
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.time.Duration;
import java.util.*;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -28,7 +29,6 @@ public class ActiveVoteToMute {
private static final Component prefix = Utility.parseMiniMessage("<gold>[VoteMute]</gold>");
private final Player votedPlayer;
private final Player startedByPlayer;
private HashSet<UUID> votedFor = new HashSet<>();
private HashSet<UUID> votedAgainst = new HashSet<>();
private int totalEligibleVoters;
@@ -36,7 +36,6 @@ public class ActiveVoteToMute {
private final RegisteredServer server;
private final ProxyServer proxyServer;
private final Component chatLogs;
private boolean endedVote = false;
public static Optional<ActiveVoteToMute> getInstance(String username) {
if (!instances.containsKey(username))
@@ -75,7 +74,7 @@ public class ActiveVoteToMute {
}
public ActiveVoteToMute(@NotNull Player votedPlayer, @NotNull RegisteredServer server, ProxyServer proxyServer, Duration duration,
int totalEligibleVoters, boolean countLowerRanks, Component chatLogs, @NotNull Player startedByPlayer) {
int totalEligibleVoters, boolean countLowerRanks, Component chatLogs) {
this.chatLogs = chatLogs;
this.votedPlayer = votedPlayer;
this.totalEligibleVoters = totalEligibleVoters;
@@ -86,7 +85,6 @@ public class ActiveVoteToMute {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule(this::endVote,
duration.toMinutes(), TimeUnit.MINUTES);
this.startedByPlayer = startedByPlayer;
}
private RegisteredServer getServer() {
@@ -94,14 +92,12 @@ public class ActiveVoteToMute {
}
private void endVote() {
if (endedVote)
return;
instances.remove(votedPlayer.getUsername());
if (votePassed()) {
mutePlayer();
return;
}
Component message = Utility.parseMiniMessage("<prefix> <red>The vote to mute <player> has failed, they will not be muted.</red>",
Component message = Utility.parseMiniMessage("<prefix> <red>The vote to mute <player> has failed, they will not be muted.</green>",
Placeholder.component("prefix", prefix), Placeholder.parsed("player", votedPlayer.getUsername()));
server.getPlayersConnected().stream()
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
@@ -122,7 +118,6 @@ public class ActiveVoteToMute {
if (!votePassed()) {
return;
}
endedVote = true;
instances.remove(votedPlayer.getUsername());
mutePlayer();
} else {
@@ -133,10 +128,7 @@ public class ActiveVoteToMute {
public boolean votePassed() {
double totalVotes = (votedFor.size() + votedAgainst.size());
if (totalVotes == 0 || votedFor.isEmpty()) {
return false;
}
if (totalVotes / totalEligibleVoters < 0.6) {
if (totalVotes / totalEligibleVoters < 0.6) {
return false;
}
return votedFor.size() / totalVotes > 0.6;
@@ -153,7 +145,7 @@ public class ActiveVoteToMute {
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
.forEach(player -> player.sendMessage(message));
proxyServer.getCommandManager().executeAsync(proxyServer.getConsoleCommandSource(),
String.format("tempmute %s 1h Muted by the community - under review. -p", votedPlayer.getUsername()));
String.format("tempmute %s 1h Muted by the community - under review.", votedPlayer.getUsername()));
String chatLogsString = PlainTextComponentSerializer.plainText().serialize(chatLogs);
@@ -181,10 +173,7 @@ public class ActiveVoteToMute {
false);
embedBuilder.addField("Server",
server.getServerInfo().getName().substring(0, 1).toUpperCase() + server.getServerInfo().getName().substring(1),
true);
embedBuilder.addField("Started by",
String.format("Username: %s\nUUID: %s", startedByPlayer.getUsername(), startedByPlayer.getUniqueId().toString()),
true);
false);
return embedBuilder;
}
@@ -215,7 +204,7 @@ public class ActiveVoteToMute {
private Component getVoteStartMessage() {
return Utility.parseMiniMessage(
String.format("""
<prefix> <green>A vote to mute <player> for one hour has been started, please read the logs below before voting.</green>
<prefix> <gold>[VoteMute]</gold> <green>A vote to mute <player> for one hour has been started, please read the logs below before voting.</green>
<logs>
<prefix> Click: <click:run_command:'/votetomutehelper vote %s yes'><red>Mute</red></click> --- <click:run_command:'/votetomutehelper vote %s no'><yellow>Don't mute</yellow></click>""",
votedPlayer.getUsername(), votedPlayer.getUsername()),
@@ -223,16 +212,4 @@ public class ActiveVoteToMute {
Placeholder.parsed("player", votedPlayer.getUsername()),
Placeholder.component("logs", chatLogs));
}
public Player getVotedPlayer() {
return votedPlayer;
}
public int getVotedFor() {
return votedFor.size();
}
public int getTotalEligibleVoters() {
return totalEligibleVoters;
}
}
@@ -42,7 +42,7 @@ public class VoteToMuteStarter {
}
public void start() {
chatLogHandler.retrieveChatLogs(votedPlayer.getUniqueId(), Duration.ofMinutes(10), serverName).whenCompleteAsync((chatLogs, throwable) -> {
chatLogHandler.retrieveChatLogs(votedPlayer.getUniqueId(), Duration.ofMinutes(5), serverName).whenCompleteAsync((chatLogs, throwable) -> {
if (throwable != null) {
commandSource.sendMessage(Utility.parseMiniMessage("<prefix> <red>Unable to retrieve messages</red> for player <player>",
Placeholder.component("prefix", prefix),
@@ -52,41 +52,40 @@ public class VoteToMuteStarter {
parseChatLogs(chatLogs);
commandSource.sendMessage(Utility.parseMiniMessage(
"<prefix> <green>Please select up to 10 messages other players should see to decide their vote, seperated by comma's. " +
"Example: <gold>/votetomutehelper messages 1, 2, 5, 8</gold></green>", Placeholder.component("prefix", prefix)));
showPage(1);
"Example: <gold>/votetomutehelper messages 1, 2, 5, 8</gold></green>"));
showPage(0);
});
}
private void parseChatLogs(List<ChatLog> chatLogs) {
TagResolver.Single playerTag = Placeholder.parsed("player", votedPlayer.getUsername());
TagResolver.Single prefixTag = Placeholder.component("prefix", prefix);
chatLogs.sort(Comparator.comparing(ChatLog::getTimestamp).reversed());
chatLogs.sort(Comparator.comparing(ChatLog::getTimestamp));
parsedChatLogs = IntStream.range(0, chatLogs.size())
.mapToObj(i -> Utility.parseMiniMessage(
"<number>. <prefix> <player>: <message>",
"<number>. [ChatLog] <player>: <message>",
TagResolver.resolver(
Placeholder.unparsed("message", chatLogs.get(i).getMessage()),
Placeholder.parsed("number", String.valueOf(i + 1)),
playerTag, prefixTag
playerTag
))
)
.toList();
}
public void showPage(int page) {
List<Component> collect = parsedChatLogs.stream().skip((page - 1) * 10L).limit(10L).toList();
List<Component> collect = parsedChatLogs.stream().skip(page * 10L).limit(page).toList();
Component chatLogsComponent = Component.join(JoinConfiguration.newlines(), collect);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<prefix> ChatLogs for <player>\n<logs>\n");
if (page > 1) {
stringBuilder.append("<click:run_command:/votetomutehelper page ")
if (page != 0) {
stringBuilder.append("<click:run_command:/votetomute page ")
.append(page - 1)
.append("><hover:show_text:'<gold>Click to go to previous page'><gold><previous page></gold></hover></click> ");
}
if (parsedChatLogs.size() > page * 10) {
stringBuilder.append("<click:run_command:/votetomutehelper page ")
stringBuilder.append("<click:run_command:/votetomute page ")
.append(page + 1)
.append("><hover:show_text:'<gold>Click to go to next page'><gold><next page></gold></hover></click> ");
.append("><hover:show_text:'<gold>Click to go to next page'><gold><previous page></gold></hover></click> ");
}
commandSource.sendMessage(Utility.parseMiniMessage(stringBuilder.toString(),
Placeholder.parsed("player", votedPlayer.getUsername()),