Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e437079e4 | ||
|
|
45eceaf4a6 | ||
|
|
c5eebb7e88 | ||
|
|
5e34e6e7fa | ||
|
|
c5c60d25b1 | ||
|
|
804e619e8a | ||
|
|
b3e545a0c8 | ||
|
|
773638c222 | ||
|
|
73e67eed6c | ||
|
|
c9e819645a | ||
|
|
33dc113b36 | ||
|
|
ba8ba373fd | ||
|
|
c3e27edb7e | ||
|
|
bd48c3f0ca | ||
|
|
71a8c652dc | ||
|
|
0877d05296 | ||
|
|
e1d6da56d0 | ||
|
|
13e4966a12 | ||
|
|
340975e99e | ||
|
|
3ea078237d | ||
|
|
2c3feac367 | ||
|
|
c97f5a54d7 | ||
|
|
99f66c3b4b | ||
|
|
de24ca4437 | ||
|
|
e351e86bea | ||
|
|
4e92285261 | ||
|
|
a377bdfe48 | ||
|
|
4af85d0e79 | ||
|
|
034de90062 | ||
|
|
bd8fa02f1e |
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.alttd:Galaxy-API:1.19.2-R0.1-SNAPSHOT") {
|
||||
// compileOnly("com.alttd:Galaxy-API:1.21-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(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "ChatPlugin");
|
||||
CONFIGPATH = new File(File.separator + "mnt" + File.separator + "configs" + File.separator + "ChatPlugin");
|
||||
CONFIG_FILE = new File(CONFIGPATH, "config.yml");
|
||||
configLoader = YamlConfigurationLoader.builder()
|
||||
.file(CONFIG_FILE)
|
||||
@@ -543,4 +543,20 @@ public final class Config {
|
||||
NICK_ALLOWED_COLOR_CODESLIST = getList("nicknames.allowed-color-codes", List.of("&0", "&1", "&2", "&3", "&4", "&5", "&6", "&7", "&8", "&9", "&a", "&b", "&c", "&d", "&e", "&f", "&r"));
|
||||
NICK_CURRENT = getString("nicknames.messages.nick-current", NICK_CURRENT);
|
||||
}
|
||||
|
||||
public static int DEATH_MESSAGES_MAX_PER_PERIOD = 5;
|
||||
public static int DEATH_MESSAGES_LIMIT_PERIOD_MINUTES = 15;
|
||||
|
||||
private static void deathMessagesSettings() {
|
||||
DEATH_MESSAGES_MAX_PER_PERIOD = getInt("death-messages.max-per-period", DEATH_MESSAGES_MAX_PER_PERIOD);
|
||||
DEATH_MESSAGES_LIMIT_PERIOD_MINUTES = getInt("death-messages.limit-period-minutes", DEATH_MESSAGES_LIMIT_PERIOD_MINUTES);
|
||||
}
|
||||
|
||||
public static long CHAT_LOG_DELETE_OLDER_THAN_DAYS = 31;
|
||||
public static long CHAT_LOG_SAVE_DELAY_MINUTES = 5;
|
||||
|
||||
private static void chatLogSettings() {
|
||||
CHAT_LOG_DELETE_OLDER_THAN_DAYS = getLong("chat-log.delete-older-than-days", CHAT_LOG_DELETE_OLDER_THAN_DAYS);
|
||||
CHAT_LOG_SAVE_DELAY_MINUTES = getLong("chat-log.save-delay-minutes", CHAT_LOG_SAVE_DELAY_MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,13 +178,18 @@ public final class RegexConfig {
|
||||
String regex = entry.getValue().node("regex").getString();
|
||||
String replacement = entry.getValue().node("replacement").getString();
|
||||
List<String> exclusions = entry.getValue().node("exclusions").getList(io.leangen.geantyref.TypeToken.get(String.class), new ArrayList<>());
|
||||
boolean disableInPrivate = false;
|
||||
ConfigurationNode node = entry.getValue().node("disable-in-private");
|
||||
if (node != null) {
|
||||
disableInPrivate = node.getBoolean();
|
||||
}
|
||||
if (type == null || type.isEmpty() || regex == null || regex.isEmpty()) {
|
||||
ALogger.warn("Filter: " + name + " was set up incorrectly");
|
||||
} else {
|
||||
if (replacement == null || replacement.isEmpty()) {
|
||||
replacement = name;
|
||||
}
|
||||
ChatFilter chatFilter = new ChatFilter(name, type, regex, replacement, exclusions);
|
||||
ChatFilter chatFilter = new ChatFilter(name, type, regex, replacement, exclusions, disableInPrivate);
|
||||
RegexManager.addFilter(chatFilter);
|
||||
}
|
||||
} catch(SerializationException ex) {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.alttd.chat.database;
|
||||
|
||||
import com.alttd.chat.objects.chat_log.ChatLog;
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class ChatLogQueries {
|
||||
|
||||
protected static void createChatLogTable() {
|
||||
String nicknamesTableQuery = "CREATE TABLE IF NOT EXISTS chat_log("
|
||||
+ "uuid CHAR(48) NOT NULL,"
|
||||
+ "time_stamp TIMESTAMP(6) NOT NULL, "
|
||||
+ "server VARCHAR(50) NOT NULL, "
|
||||
+ "chat_message VARCHAR(300) NOT NULL, "
|
||||
+ "blocked BIT(1) NOT NULL DEFAULT 0"
|
||||
+ ")";
|
||||
|
||||
try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(nicknamesTableQuery)) {
|
||||
preparedStatement.executeUpdate();
|
||||
} catch (Throwable throwable) {
|
||||
ALogger.error("Failed to create chat log table", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull CompletableFuture<Boolean> storeMessages(HashMap<UUID, List<ChatLog>> chatMessages) {
|
||||
String insertQuery = "INSERT INTO chat_log (uuid, time_stamp, server, chat_message, blocked) VALUES (?, ?, ?, ?, ?)";
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = DatabaseConnection.createTransactionConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(insertQuery);
|
||||
for (List<ChatLog> chatLogList : chatMessages.values()) {
|
||||
for (ChatLog chatLog : chatLogList) {
|
||||
chatLog.prepareStatement(preparedStatement);
|
||||
preparedStatement.addBatch();
|
||||
}
|
||||
}
|
||||
int[] updatedRowsCount = preparedStatement.executeBatch();
|
||||
boolean isSuccess = Arrays.stream(updatedRowsCount).allMatch(i -> i >= 0);
|
||||
|
||||
if (isSuccess) {
|
||||
connection.commit();
|
||||
return true;
|
||||
} else {
|
||||
connection.rollback();
|
||||
ALogger.warn("Failed to store messages");
|
||||
return false;
|
||||
}
|
||||
} catch (SQLException sqlException) {
|
||||
ALogger.error("Failed to store chat messages", sqlException);
|
||||
throw new CompletionException("Failed to store chat messages", sqlException);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static @NotNull CompletableFuture<List<ChatLog>> retrieveMessages(ChatLogHandler chatLogHandler, UUID uuid, Duration duration, String server) {
|
||||
String query = "SELECT * FROM chat_log WHERE uuid = ? AND time_stamp > ? AND server = ?";
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = DatabaseConnection.getConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(query);
|
||||
preparedStatement.setString(1, uuid.toString());
|
||||
preparedStatement.setTimestamp(2, Timestamp.from(Instant.now().minus(duration)));
|
||||
preparedStatement.setString(3, server);
|
||||
ResultSet resultSet = preparedStatement.executeQuery();
|
||||
List<ChatLog> chatLogs = new ArrayList<>();
|
||||
while (resultSet.next()) {
|
||||
ChatLog chatLog = chatLogHandler.loadFromResultSet(resultSet);
|
||||
chatLogs.add(chatLog);
|
||||
}
|
||||
return chatLogs;
|
||||
} catch (SQLException sqlException) {
|
||||
ALogger.error(String.format("Failed to retrieve messages for user %s", uuid), sqlException);
|
||||
throw new CompletionException(String.format("Failed to retrieve messages for user %s", uuid), sqlException);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static CompletableFuture<Boolean> deleteOldMessages(Duration duration) {
|
||||
String query = "DELETE FROM chat_log WHERE time_stamp < ?";
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = DatabaseConnection.getConnection()) {
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(query);
|
||||
preparedStatement.setTimestamp(1, Timestamp.from(Instant.now().minus(duration)));
|
||||
return preparedStatement.execute();
|
||||
} catch (SQLException sqlException) {
|
||||
ALogger.error(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException);
|
||||
throw new CompletionException(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.alttd.chat.database;
|
||||
|
||||
|
||||
import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
@@ -66,6 +65,21 @@ public class DatabaseConnection {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transactional database connection.
|
||||
*
|
||||
* @return A {@code Connection} object representing the transactional database connection.
|
||||
* @throws SQLException If there is an error creating the database connection.
|
||||
*/
|
||||
public static Connection createTransactionConnection() throws SQLException {
|
||||
connection = DriverManager.getConnection(
|
||||
"jdbc:mysql://" + Config.IP + ":" + Config.PORT + "/" + Config.DATABASE + "?autoReconnect=true"+
|
||||
"&useSSL=false",
|
||||
Config.USERNAME, Config.PASSWORD);
|
||||
connection.setAutoCommit(false);
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection for this instance
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ public class Queries {
|
||||
tables.add("CREATE TABLE IF NOT EXISTS mails (`id` INT NOT NULL AUTO_INCREMENT, `uuid` VARCHAR(36) NOT NULL, `sender` VARCHAR(36) NOT NULL, `message` VARCHAR(256) NOT NULL, `sendtime` BIGINT default 0, `readtime` BIGINT default 0, PRIMARY KEY (`id`))");
|
||||
createNicknamesTable();
|
||||
createRequestedNicknamesTable();
|
||||
ChatLogQueries.createChatLogTable();
|
||||
try {
|
||||
Connection connection = DatabaseConnection.getConnection();
|
||||
|
||||
|
||||
@@ -8,10 +8,13 @@ import com.alttd.chat.objects.ModifiableString;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import net.luckperms.api.cacheddata.CachedPermissionData;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RegexManager {
|
||||
@@ -43,17 +46,24 @@ public class RegexManager {
|
||||
}
|
||||
|
||||
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, String channel) { // TODO loop all objects in the list and check if they violate based on the MATCHER
|
||||
return filterText(playerName, uuid, modifiableString, true, channel);
|
||||
return filterText(playerName, uuid, modifiableString, true, channel, null);
|
||||
}
|
||||
|
||||
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, boolean matcher, String channel) {
|
||||
return filterText(playerName, uuid, modifiableString, matcher, channel, null);
|
||||
}
|
||||
|
||||
public static boolean filterText(String playerName, UUID uuid, ModifiableString modifiableString, boolean matcher, String channel, Consumer<FilterType> filterAction) {
|
||||
User user = ChatAPI.get().getLuckPerms().getUserManager().getUser(uuid);
|
||||
if (user == null) {
|
||||
ALogger.warn("Tried to check chat filters for a user who doesn't exist in LuckPerms");
|
||||
return false;
|
||||
}
|
||||
CachedPermissionData permissionData = user.getCachedData().getPermissionData();
|
||||
boolean isPrivate = channel.equals("party");
|
||||
for(ChatFilter chatFilter : chatFilters) {
|
||||
if (isPrivate && chatFilter.isDisabledInPrivate())
|
||||
continue;
|
||||
switch (chatFilter.getType()) {
|
||||
case CHAT:
|
||||
break;
|
||||
@@ -74,6 +84,25 @@ public class RegexManager {
|
||||
chatFilter.replaceMatcher(modifiableString);
|
||||
}
|
||||
break;
|
||||
case PUNISH:
|
||||
if (permissionData.checkPermission("chat.bypass-punish").asBoolean())
|
||||
break;
|
||||
if (chatFilter.matches(modifiableString)) {
|
||||
ALogger.info(playerName + " triggered the punish filter for " + chatFilter.getName()
|
||||
+ " with: " + modifiableString.string() + ".");
|
||||
|
||||
if (filterAction == null){
|
||||
ALogger.info("No filterAction was provided, not doing anything");
|
||||
return false;
|
||||
}
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player == null) {
|
||||
ALogger.warn("Tried to punish a player who triggered the filter, but the player is offline.");
|
||||
return false;
|
||||
}
|
||||
filterAction.accept(FilterType.PUNISH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.alttd.chat.objects;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface BatchInsertable {
|
||||
|
||||
void prepareStatement(PreparedStatement preparedStatement) throws SQLException;
|
||||
|
||||
}
|
||||
@@ -17,14 +17,16 @@ public class ChatFilter {
|
||||
private final Pattern pattern;
|
||||
private final String replacement;
|
||||
private final List<String> exclusions;
|
||||
private final boolean disableInPrivate;
|
||||
|
||||
public ChatFilter(String name, String type, String regex, String replacement, List<String> exclusions) {
|
||||
public ChatFilter(String name, String type, String regex, String replacement, List<String> exclusions, boolean disableInPrivate) {
|
||||
this.name = name;
|
||||
this.filterType = FilterType.getType(type);
|
||||
this.regex = regex;
|
||||
this.pattern = Pattern.compile(getRegex(), Pattern.CASE_INSENSITIVE);
|
||||
this.replacement = replacement;
|
||||
this.exclusions = exclusions;
|
||||
this.disableInPrivate = disableInPrivate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -47,6 +49,10 @@ public class ChatFilter {
|
||||
return this.exclusions;
|
||||
}
|
||||
|
||||
public boolean isDisabledInPrivate() {
|
||||
return disableInPrivate;
|
||||
}
|
||||
|
||||
public boolean matches(ModifiableString filterableString) {
|
||||
String input = filterableString.string();
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
|
||||
@@ -5,7 +5,8 @@ public enum FilterType {
|
||||
EMOTE("emote"),
|
||||
CHAT("chat"),
|
||||
REPLACEMATCHER("replacematcher"),
|
||||
BLOCK("block");
|
||||
BLOCK("block"),
|
||||
PUNISH("punish");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.alttd.chat.objects.chat_log;
|
||||
|
||||
import com.alttd.chat.objects.BatchInsertable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChatLog implements BatchInsertable {
|
||||
|
||||
private final UUID uuid;
|
||||
private final Instant timestamp;
|
||||
private final String server;
|
||||
private final String message;
|
||||
private final boolean blocked;
|
||||
|
||||
protected ChatLog(UUID uuid, Instant timestamp, String server, String message, boolean blocked) {
|
||||
this.uuid = uuid;
|
||||
this.timestamp = timestamp;
|
||||
this.server = server;
|
||||
this.message = message;
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareStatement(@NotNull PreparedStatement preparedStatement) throws SQLException {
|
||||
preparedStatement.setString(1, uuid.toString());
|
||||
preparedStatement.setTimestamp(2, Timestamp.from(timestamp));
|
||||
preparedStatement.setString(3, server);
|
||||
preparedStatement.setString(4, message);
|
||||
preparedStatement.setInt(5, blocked ? 1 : 0);
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Instant getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.alttd.chat.objects.chat_log;
|
||||
|
||||
import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.database.ChatLogQueries;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ChatLogHandler {
|
||||
|
||||
private static ChatLogHandler instance = null;
|
||||
private ScheduledExecutorService executorService = null;
|
||||
|
||||
public static ChatLogHandler getInstance(boolean enableLogging) {
|
||||
if (instance == null)
|
||||
instance = new ChatLogHandler(enableLogging);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private boolean isSaving;
|
||||
private final Queue<ChatLog> chatLogQueue = new ConcurrentLinkedQueue<>();
|
||||
private final HashMap<UUID, List<ChatLog>> chatLogs = new HashMap<>();
|
||||
|
||||
public ChatLogHandler(boolean enableLogging) {
|
||||
if (!enableLogging) {
|
||||
ALogger.info("Logging is not enabled on this server.");
|
||||
return;
|
||||
}
|
||||
Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS);
|
||||
ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> {
|
||||
if (success) {
|
||||
ALogger.info(String.format("Deleted all messages older than %s days from chat log database.", deleteThreshold.toDays()));
|
||||
} else {
|
||||
ALogger.warn(String.format("Failed to delete all messages older than %s days from chat log database.", deleteThreshold.toDays()));
|
||||
}
|
||||
});
|
||||
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));
|
||||
},
|
||||
Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES);
|
||||
ALogger.info("Logging has started!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the executor service and saves the chat logs to the database.
|
||||
* Will throw an error if called on a ChatLogHandler that was started without logging
|
||||
*/
|
||||
public void shutDown() {
|
||||
executorService.shutdown();
|
||||
saveToDatabase(true);
|
||||
}
|
||||
|
||||
private synchronized void savingToDatabase(boolean saving) {
|
||||
isSaving = saving;
|
||||
}
|
||||
|
||||
private synchronized boolean isBlocked() {
|
||||
return isSaving;
|
||||
}
|
||||
|
||||
public synchronized void addLog(ChatLog chatLog) {
|
||||
if (isBlocked()) {
|
||||
chatLogQueue.add(chatLog);
|
||||
} else {
|
||||
chatLogs.computeIfAbsent(chatLog.getUuid(), k -> new ArrayList<>()).add(chatLog);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
public ChatLog loadFromResultSet(@NotNull ResultSet resultSet) throws SQLException {
|
||||
UUID chatLogUUID = UUID.fromString(resultSet.getString("uuid"));
|
||||
Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant();
|
||||
String server = resultSet.getString("server");
|
||||
String chatMessage = resultSet.getString("chat_message");
|
||||
boolean chatMessageBlocked = resultSet.getInt("blocked") == 1;
|
||||
return new ChatLog(chatLogUUID, chatTimestamp, server, chatMessage, chatMessageBlocked);
|
||||
}
|
||||
|
||||
public void addChatLog(UUID uuid, String server, String message, boolean blocked) {
|
||||
addLog(new ChatLog(uuid, Instant.now(), server, message, blocked));
|
||||
}
|
||||
|
||||
public CompletableFuture<List<ChatLog>> retrieveChatLogs(UUID uuid, Duration duration, String server) {
|
||||
List<ChatLog> chatLogList = chatLogs.getOrDefault(uuid, new ArrayList<>());
|
||||
return ChatLogQueries.retrieveMessages(this, uuid, duration, server)
|
||||
.thenCompose(chatLogs -> CompletableFuture.supplyAsync(() -> {
|
||||
chatLogList.addAll(chatLogs);
|
||||
return chatLogList;
|
||||
}))
|
||||
.exceptionally(ex -> {
|
||||
throw new CompletionException(ex);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,10 @@ public class ALogger {
|
||||
logger.warn(message);
|
||||
}
|
||||
|
||||
public static void warn(String message, Throwable throwable) {
|
||||
logger.warn(message, throwable);
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
logger.info(message);
|
||||
}
|
||||
@@ -23,4 +27,8 @@ public class ALogger {
|
||||
public static void error(String message) {
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
public static void error(String message, Throwable throwable) {
|
||||
logger.error(message, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-15
@@ -1,25 +1,12 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
id("com.github.johnrengelman.shadow") version "7.1.0"
|
||||
id("io.github.goooler.shadow") version "8.1.8"
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -27,7 +14,7 @@ subprojects {
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-36
@@ -1,58 +1,25 @@
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("xyz.jpenilla.run-paper") version "1.0.6"
|
||||
id("io.github.goooler.shadow")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":api")) // API
|
||||
compileOnly("com.alttd:Galaxy-API:1.20.4-R0.1-SNAPSHOT") // Galaxy
|
||||
// compileOnly("com.alttd:Galaxy-API:1.21-R0.1-SNAPSHOT") // Galaxy
|
||||
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,12 @@ import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.config.ServerConfig;
|
||||
import com.alttd.chat.database.DatabaseConnection;
|
||||
import com.alttd.chat.handler.ChatHandler;
|
||||
import com.alttd.chat.listeners.BookListener;
|
||||
import com.alttd.chat.listeners.ChatListener;
|
||||
import com.alttd.chat.listeners.PlayerListener;
|
||||
import com.alttd.chat.listeners.PluginMessage;
|
||||
import com.alttd.chat.listeners.*;
|
||||
import com.alttd.chat.nicknames.Nicknames;
|
||||
import com.alttd.chat.nicknames.NicknamesEvents;
|
||||
import com.alttd.chat.objects.channels.Channel;
|
||||
import com.alttd.chat.objects.channels.CustomChannel;
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -40,8 +38,9 @@ public class ChatPlugin extends JavaPlugin {
|
||||
chatAPI = new ChatImplementation();
|
||||
chatHandler = new ChatHandler();
|
||||
DatabaseConnection.initialize();
|
||||
serverConfig = new ServerConfig(Bukkit.getServerName());
|
||||
registerListener(new PlayerListener(serverConfig), new ChatListener(), new BookListener());
|
||||
serverConfig = new ServerConfig(Bukkit.getServer().getName());
|
||||
ChatLogHandler chatLogHandler = ChatLogHandler.getInstance(true);
|
||||
registerListener(new PlayerListener(serverConfig), new ChatListener(chatLogHandler), new BookListener(), new ShutdownListener(chatLogHandler, this));
|
||||
if(serverConfig.GLOBALCHAT) {
|
||||
registerCommand("globalchat", new GlobalChat());
|
||||
registerCommand("toggleglobalchat", new ToggleGlobalChat());
|
||||
@@ -120,7 +119,7 @@ public class ChatPlugin extends JavaPlugin {
|
||||
public void ReloadConfig() {
|
||||
chatAPI.ReloadConfig();
|
||||
chatAPI.ReloadChatFilters();
|
||||
serverConfig = new ServerConfig(Bukkit.getServerName());
|
||||
serverConfig = new ServerConfig(Bukkit.getServer().getName());
|
||||
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.sendMiniMessage(Config.CUSTOM_CHANNEL_TOGGLED, TagResolver.resolver(
|
||||
player.sendRichMessage(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.sendMiniMessage(Config.PARTY_TOGGLED, Placeholder.component("status",
|
||||
player.sendRichMessage(Config.PARTY_TOGGLED, Placeholder.component("status",
|
||||
toggle(player.getUniqueId()) ? Config.TOGGLED_ON : Config.TOGGLED_OFF));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ChatHandler {
|
||||
|
||||
@@ -130,7 +131,7 @@ public class ChatHandler {
|
||||
Placeholder.component("sender", senderName),
|
||||
Placeholder.component("prefix", prefix),
|
||||
Placeholder.component("message", parseMessageContent(player, message)),
|
||||
Placeholder.parsed("server", Bukkit.getServerName())
|
||||
Placeholder.parsed("server", Bukkit.getServer().getName())
|
||||
);
|
||||
|
||||
Component component = Utility.parseMiniMessage(Config.GCFORMAT, placeholders);
|
||||
@@ -164,7 +165,7 @@ public class ChatHandler {
|
||||
TagResolver placeholders = TagResolver.resolver(
|
||||
Placeholder.component("sender", senderName),
|
||||
Placeholder.component("message", parseMessageContent(player, message)),
|
||||
Placeholder.parsed("server", Bukkit.getServerName()),
|
||||
Placeholder.parsed("server", Bukkit.getServer().getName()),
|
||||
Placeholder.parsed("channel", channel.getChannelName())
|
||||
);
|
||||
Component component = Utility.parseMiniMessage(channel.getFormat(), placeholders);
|
||||
@@ -237,12 +238,21 @@ public class ChatHandler {
|
||||
}
|
||||
|
||||
private void sendChatChannelMessage(CustomChannel chatChannel, UUID uuid, Component component) {
|
||||
if (!chatChannel.getServers().contains(Bukkit.getServerName())) return;
|
||||
if (!chatChannel.getServers().contains(Bukkit.getServer().getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getServer().getOnlinePlayers().stream()
|
||||
.filter(p -> p.hasPermission(chatChannel.getPermission()))
|
||||
.filter(p -> !ChatUserManager.getChatUser(p.getUniqueId()).getIgnoredPlayers().contains(uuid))
|
||||
.forEach(p -> p.sendMessage(component));
|
||||
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));
|
||||
}
|
||||
|
||||
private void sendPluginMessage(Player player, String channel, Component component) {
|
||||
|
||||
@@ -5,13 +5,13 @@ import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.handler.ChatHandler;
|
||||
import com.alttd.chat.managers.ChatUserManager;
|
||||
import com.alttd.chat.managers.RegexManager;
|
||||
import com.alttd.chat.objects.ChatFilter;
|
||||
import com.alttd.chat.objects.ChatUser;
|
||||
import com.alttd.chat.objects.ModifiableString;
|
||||
import com.alttd.chat.objects.Toggleable;
|
||||
import com.alttd.chat.objects.*;
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import com.alttd.chat.util.GalaxyUtility;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
@@ -32,12 +32,19 @@ 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 {
|
||||
|
||||
private final PlainTextComponentSerializer plainTextComponentSerializer = PlainTextComponentSerializer.plainText();
|
||||
private final ChatLogHandler chatLogHandler;
|
||||
|
||||
public ChatListener(ChatLogHandler chatLogHandler) {
|
||||
this.chatLogHandler = chatLogHandler;
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
@@ -76,25 +83,42 @@ public class ChatListener implements Listener {
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
|
||||
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());
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
Component input = event.message().colorIfAbsent(NamedTextColor.WHITE);
|
||||
|
||||
ModifiableString modifiableString = new ModifiableString(input);
|
||||
|
||||
// todo a better way for this
|
||||
if(!RegexManager.filterText(player.getName(), player.getUniqueId(), modifiableString, "chat")) {
|
||||
if(!RegexManager.filterText(player.getName(), uuid, 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;
|
||||
}
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("punish");
|
||||
out.writeUTF(player.getName());
|
||||
out.writeUTF(uuid.toString());
|
||||
out.writeUTF(modifiableString.string());
|
||||
player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray());
|
||||
})) {
|
||||
event.setCancelled(true);
|
||||
GalaxyUtility.sendBlockedNotification("Language", player,
|
||||
modifiableString.component(),
|
||||
"");
|
||||
chatLogHandler.addChatLog(uuid, player.getServer().getName(), 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);
|
||||
|
||||
@@ -105,6 +129,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);
|
||||
ALogger.info(PlainTextComponentSerializer.plainText().serialize(input));
|
||||
}
|
||||
|
||||
@@ -118,11 +143,12 @@ 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(onlinePlayer.displayName()))
|
||||
.replacement(mention.append(onlinePlayerUser.getDisplayName()))
|
||||
.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()
|
||||
@@ -132,15 +158,17 @@ public class ChatListener implements Listener {
|
||||
// String substring = a.group().substring(1);
|
||||
// return ;
|
||||
// });
|
||||
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()))
|
||||
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId())
|
||||
&& !player.hasPermission("chat.ignorebypass"))
|
||||
playersToPing.add(onlinePlayer);
|
||||
} else if (nickPattern.matcher(modifiableString.string()).find()) {
|
||||
modifiableString.replace(TextReplacementConfig.builder()
|
||||
.once()
|
||||
.match(nickPattern)
|
||||
.replacement(mention.append(onlinePlayer.displayName()))
|
||||
.replacement(mention.append(onlinePlayerUser.getDisplayName()))
|
||||
.build());
|
||||
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()))
|
||||
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId())
|
||||
&& !player.hasPermission("chat.ignorebypass"))
|
||||
playersToPing.add(onlinePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,27 @@ 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;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -38,8 +50,8 @@ public class PlayerListener implements Listener {
|
||||
UUID uuid = player.getUniqueId();
|
||||
Toggleable.disableToggles(uuid);
|
||||
|
||||
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())));
|
||||
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())));
|
||||
}
|
||||
|
||||
ChatUser user = ChatUserManager.getChatUser(uuid);
|
||||
@@ -86,4 +98,43 @@ public class PlayerListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<UUID, Stack<Instant>> sendPlayerDeaths = new HashMap<>();
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDeath(@NotNull PlayerDeathEvent event) {
|
||||
UUID uuid = event.getPlayer().getUniqueId();
|
||||
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)) {
|
||||
playerDeathsStack.pop();
|
||||
}
|
||||
|
||||
if (playerDeathsStack.size() > Config.DEATH_MESSAGES_MAX_PER_PERIOD || serverConfig.MUTED) {
|
||||
event.deathMessage(Component.empty());
|
||||
return;
|
||||
}
|
||||
Component component = event.deathMessage();
|
||||
|
||||
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, Player player, byte[] bytes) {
|
||||
public void onPluginMessageReceived(String channel, @NotNull Player ignored, byte[] bytes) {
|
||||
if (!channel.equals(Config.MESSAGECHANNEL)) {
|
||||
return;
|
||||
}
|
||||
@@ -38,36 +38,37 @@ public class PluginMessage implements PluginMessageListener {
|
||||
case "privatemessagein": {
|
||||
UUID uuid = UUID.fromString(in.readUTF());
|
||||
String target = in.readUTF();
|
||||
Player p = Bukkit.getPlayer(uuid);
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
String message = in.readUTF();
|
||||
UUID targetuuid = UUID.fromString(in.readUTF());
|
||||
if (p != null) {
|
||||
if (player == null) {
|
||||
break;
|
||||
}
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "privatemessageout": {
|
||||
UUID uuid = UUID.fromString(in.readUTF());
|
||||
String target = in.readUTF();
|
||||
Player p = Bukkit.getPlayer(uuid);
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
String message = in.readUTF();
|
||||
UUID targetuuid = UUID.fromString(in.readUTF());
|
||||
if (p != null) {
|
||||
if (player == null) {
|
||||
break;
|
||||
}
|
||||
ChatUser chatUser = ChatUserManager.getChatUser(uuid);
|
||||
if (!chatUser.getIgnoredPlayers().contains(targetuuid)) {
|
||||
if (isTargetNotIgnored(chatUser, targetuuid)) {
|
||||
chatUser.setReplyTarget(target);
|
||||
p.sendMessage(GsonComponentSerializer.gson().deserialize(message));
|
||||
player.sendMessage(GsonComponentSerializer.gson().deserialize(message));
|
||||
// ChatUser user = ChatUserManager.getChatUser(uuid);
|
||||
// user.setReplyTarget(target);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "globalchat": {
|
||||
@@ -78,7 +79,7 @@ public class PluginMessage implements PluginMessageListener {
|
||||
|
||||
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission(Config.GCPERMISSION)).forEach(p -> {
|
||||
ChatUser chatUser = ChatUserManager.getChatUser(p.getUniqueId());
|
||||
if (!chatUser.getIgnoredPlayers().contains(uuid)) {
|
||||
if (isTargetNotIgnored(chatUser, uuid)) {
|
||||
p.sendMessage(GsonComponentSerializer.gson().deserialize(message));
|
||||
}
|
||||
});
|
||||
@@ -193,7 +194,7 @@ public class PluginMessage implements PluginMessageListener {
|
||||
ALogger.warn("Received ChatChannel message for non existent channel.");
|
||||
return;
|
||||
}
|
||||
if (!chatChannel.getServers().contains(Bukkit.getServerName())) {
|
||||
if (!chatChannel.getServers().contains(Bukkit.getServer().getName())) {
|
||||
ALogger.warn("Received ChatChannel message for the wrong server.");
|
||||
return;
|
||||
}
|
||||
@@ -216,4 +217,15 @@ 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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.alttd.chat.listeners;
|
||||
|
||||
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) {
|
||||
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.sendMiniMessage(Config.NICK_BLOCKED_COLOR_CODES, null);
|
||||
sender.sendRichMessage(Config.NICK_BLOCKED_COLOR_CODES);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Utility.checkNickBrightEnough(nickName)) {
|
||||
sender.sendMiniMessage("<red>At least one color must be brighter than 30 for each color</red>", null);
|
||||
sender.sendRichMessage("<red>At least one color must be brighter than 30 for each color</red>");
|
||||
return false;
|
||||
}
|
||||
|
||||
String cleanNick = NickUtilities.removeAllColors(nickName);
|
||||
|
||||
if (cleanNick.length() < 3 || cleanNick.length() > 16) {
|
||||
sender.sendMiniMessage(Config.NICK_INVALID_LENGTH, null);
|
||||
sender.sendRichMessage(Config.NICK_INVALID_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cleanNick.matches("[a-zA-Z0-9_]*") || nickName.length() > 192) { //192 is if someone puts {#xxxxxx<>} in front of every letter
|
||||
sender.sendMiniMessage(Config.NICK_INVALID_CHARACTERS, null);
|
||||
sender.sendRichMessage(Config.NICK_INVALID_CHARACTERS);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ public class NickUtilities
|
||||
if (uniqueId.equals(uuid)){
|
||||
ChatPlugin.getInstance().getLogger().info(uuid + " " + uniqueId);
|
||||
}
|
||||
sender.sendMiniMessage(Config.NICK_TAKEN, null);
|
||||
sender.sendRichMessage(Config.NICK_TAKEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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;
|
||||
@@ -13,7 +11,6 @@ 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;
|
||||
@@ -114,7 +111,7 @@ public class Nicknames implements CommandExecutor, TabCompleter {
|
||||
Placeholder.component("nickname", chatUser.getDisplayName()),
|
||||
Placeholder.parsed("currentnickname", chatUser.getNickNameString())
|
||||
);
|
||||
player.sendMiniMessage(Config.NICK_CURRENT, placeholders);
|
||||
player.sendRichMessage(Config.NICK_CURRENT, placeholders);
|
||||
}
|
||||
break;
|
||||
case "help":
|
||||
@@ -384,7 +381,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) {
|
||||
@@ -399,25 +396,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,12 +1,8 @@
|
||||
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,6 +41,7 @@ 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());
|
||||
|
||||
Binary file not shown.
+12
-1
@@ -4,11 +4,14 @@ 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
|
||||
@@ -16,6 +19,14 @@ 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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
`maven-publish`
|
||||
id("com.github.johnrengelman.shadow")
|
||||
id("io.github.goooler.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.0-BETA-SNAPSHOT")
|
||||
compileOnly("com.alttd.proxydiscordlink:ProxyDiscordLink:1.0.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.alttd.chat.ChatImplementation;
|
||||
import com.alttd.chat.managers.ChatUserManager;
|
||||
import com.alttd.chat.managers.PartyManager;
|
||||
import com.alttd.chat.objects.ChatUser;
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.velocitychat.commands.*;
|
||||
import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.database.DatabaseConnection;
|
||||
@@ -23,6 +24,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.plugin.Dependency;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
@@ -108,11 +110,14 @@ public class VelocityChat {
|
||||
}
|
||||
|
||||
public void loadCommands() {
|
||||
ChatLogHandler instance = ChatLogHandler.getInstance(false);
|
||||
new SilentJoinCommand(server);
|
||||
new GlobalAdminChat(server);
|
||||
new Reload(server);
|
||||
new MailCommand(server);
|
||||
new Report(server);
|
||||
new VoteToMute(server, instance);
|
||||
new VoteToMuteHelper(server);
|
||||
server.getCommandManager().register("party", new PartyCommand());
|
||||
// all (proxy)commands go here
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.alttd.velocitychat.commands;
|
||||
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.alttd.velocitychat.commands.vote_to_mute.VoteToMuteStarter;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.command.CommandMeta;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
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 net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class VoteToMute {
|
||||
|
||||
public VoteToMute(ProxyServer proxyServer, ChatLogHandler chatLogHandler) {
|
||||
RequiredArgumentBuilder<CommandSource, String> playerNode = RequiredArgumentBuilder
|
||||
.<CommandSource, String>argument("player", StringArgumentType.string())
|
||||
.suggests((context, builder) -> {
|
||||
List<Player> possiblePlayers;
|
||||
if (context.getSource() instanceof Player player) {
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isPresent()) {
|
||||
possiblePlayers = getEligiblePlayers(currentServer.get().getServer());
|
||||
} else {
|
||||
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||
}
|
||||
} else {
|
||||
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||
}
|
||||
Collection<String> possibleValues = possiblePlayers.stream()
|
||||
.map(Player::getUsername)
|
||||
.toList();
|
||||
|
||||
if (possibleValues.isEmpty())
|
||||
return Suggestions.empty();
|
||||
|
||||
String remaining = builder.getRemaining().toLowerCase();
|
||||
possibleValues.stream()
|
||||
.filter(str -> str.toLowerCase().startsWith(remaining))
|
||||
.map(StringArgumentType::escapeIfRequired)
|
||||
.forEach(builder::suggest);
|
||||
return builder.buildFuture();
|
||||
})
|
||||
.executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
});
|
||||
|
||||
LiteralCommandNode<CommandSource> command = LiteralArgumentBuilder
|
||||
.<CommandSource>literal("votetomute")
|
||||
.requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute"))
|
||||
.requires(commandSource -> commandSource instanceof Player)
|
||||
.then(playerNode
|
||||
.suggests(((commandContext, suggestionsBuilder) -> {
|
||||
if (!(commandContext.getSource() instanceof Player player)) {
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isEmpty()) {
|
||||
sendHelpMessage(commandContext.getSource());
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
String remaining = suggestionsBuilder.getRemaining().toLowerCase();
|
||||
currentServer.get().getServer().getPlayersConnected().stream()
|
||||
.filter(connectedPlayer -> connectedPlayer.hasPermission("chat.affected-by-vote-to-mute"))
|
||||
.map(Player::getUsername)
|
||||
.filter((String str) -> str.toLowerCase().startsWith(remaining))
|
||||
.map(StringArgumentType::escapeIfRequired)
|
||||
.forEach(suggestionsBuilder::suggest);
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}))
|
||||
.executes(commandContext -> {
|
||||
String playerName = commandContext.getArgument("player", String.class);
|
||||
Optional<Player> optionalPlayer = proxyServer.getPlayer(playerName);
|
||||
if (optionalPlayer.isEmpty()) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||
"<red>Player <player> is not online.</red>",
|
||||
Placeholder.parsed("player", playerName)));
|
||||
return 1;
|
||||
}
|
||||
Player voteTarget = optionalPlayer.get();
|
||||
if (!voteTarget.hasPermission("chat.affected-by-vote-to-mute")) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||
"<red>Player <player> can not be muted by a vote.</red>",
|
||||
Placeholder.parsed("player", playerName)));
|
||||
return 1;
|
||||
}
|
||||
Player player = (Player) commandContext.getSource();
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isEmpty()) {
|
||||
sendHelpMessage(commandContext.getSource());
|
||||
return 1;
|
||||
}
|
||||
RegisteredServer server = currentServer.get().getServer();
|
||||
if (currentServer.get().getServer().getPlayersConnected().stream().anyMatch(onlinePlayer -> onlinePlayer.hasPermission("chat.staff"))) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>There is a staff member online, so vote to mute can not be used. Please contact a staff member for help instead.</red>"));
|
||||
return 1;
|
||||
}
|
||||
boolean countLowerRanks = false;
|
||||
long count = getTotalEligiblePlayers(server, false);
|
||||
if (count < 6) {
|
||||
countLowerRanks = true;
|
||||
count = getTotalEligiblePlayers(server, true);
|
||||
if (count < 6) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Not enough eligible players online to vote.</red>"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
new VoteToMuteStarter(chatLogHandler, voteTarget, player, server.getServerInfo().getName(), countLowerRanks)
|
||||
.start();
|
||||
return 1;
|
||||
}))
|
||||
.executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
})
|
||||
.build();
|
||||
|
||||
BrigadierCommand brigadierCommand = new BrigadierCommand(command);
|
||||
|
||||
CommandMeta.Builder metaBuilder = proxyServer.getCommandManager().metaBuilder(brigadierCommand);
|
||||
|
||||
CommandMeta meta = metaBuilder.build();
|
||||
|
||||
proxyServer.getCommandManager().register(meta, brigadierCommand);
|
||||
}
|
||||
|
||||
private int getTotalEligiblePlayers(RegisteredServer server, boolean countLowerRanks) {
|
||||
return (int) server.getPlayersConnected().stream()
|
||||
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
|
||||
.count();
|
||||
}
|
||||
|
||||
private void sendHelpMessage(CommandSource commandSource) {
|
||||
commandSource.sendMessage(Utility.parseMiniMessage("<red>Use: <gold>/votetomute <player></gold>.</red>"));
|
||||
}
|
||||
|
||||
private List<Player> getEligiblePlayers(ProxyServer proxyServer) {
|
||||
return proxyServer.getAllPlayers().stream()
|
||||
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Player> getEligiblePlayers(RegisteredServer registeredServer) {
|
||||
return registeredServer.getPlayersConnected().stream()
|
||||
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package com.alttd.velocitychat.commands;
|
||||
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute;
|
||||
import com.alttd.velocitychat.commands.vote_to_mute.VoteToMuteStarter;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.command.CommandMeta;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
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;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
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())
|
||||
.suggests((context, builder) -> {
|
||||
List<Player> possiblePlayers;
|
||||
if (context.getSource() instanceof Player player) {
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isPresent()) {
|
||||
possiblePlayers = getEligiblePlayers(currentServer.get().getServer());
|
||||
} else {
|
||||
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||
}
|
||||
} else {
|
||||
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||
}
|
||||
Collection<String> possibleValues = possiblePlayers.stream()
|
||||
.map(Player::getUsername)
|
||||
.toList();
|
||||
|
||||
if (possibleValues.isEmpty())
|
||||
return Suggestions.empty();
|
||||
|
||||
String remaining = builder.getRemaining().toLowerCase();
|
||||
possibleValues.stream()
|
||||
.filter(str -> str.toLowerCase().startsWith(remaining))
|
||||
.map(StringArgumentType::escapeIfRequired)
|
||||
.forEach(builder::suggest);
|
||||
return builder.buildFuture();
|
||||
})
|
||||
.executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
});
|
||||
|
||||
RequiredArgumentBuilder<CommandSource, String> yesNoNode = RequiredArgumentBuilder.
|
||||
<CommandSource, String>argument("yesNo", StringArgumentType.string())
|
||||
.suggests(((commandContext, suggestionsBuilder) -> {
|
||||
List<String> yesNoValues = Arrays.asList("yes", "no");
|
||||
String remaining = suggestionsBuilder.getRemaining().toLowerCase();
|
||||
yesNoValues.stream()
|
||||
.filter((String str) -> str.toLowerCase().startsWith(remaining))
|
||||
.map(StringArgumentType::escapeIfRequired)
|
||||
.forEach(suggestionsBuilder::suggest);
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}));
|
||||
|
||||
LiteralArgumentBuilder<CommandSource> pageNode = LiteralArgumentBuilder
|
||||
.<CommandSource>literal("page")
|
||||
.requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute"))
|
||||
.then(RequiredArgumentBuilder.<CommandSource, Integer>argument("page number", IntegerArgumentType.integer(1))
|
||||
.suggests(((commandContext, suggestionsBuilder) -> {
|
||||
if (!(commandContext.getSource() instanceof Player player)) {
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
Optional<VoteToMuteStarter> instance = VoteToMuteStarter.getInstance(player.getUniqueId());
|
||||
if (instance.isEmpty()) {
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}
|
||||
VoteToMuteStarter voteToMuteStarter = instance.get();
|
||||
String remaining = suggestionsBuilder.getRemaining().toLowerCase();
|
||||
int totalPages = voteToMuteStarter.getTotalPages();
|
||||
IntStream.range(1, totalPages + 1)
|
||||
.mapToObj(String::valueOf)
|
||||
.filter((String str) -> str.toLowerCase().startsWith(remaining))
|
||||
.map(StringArgumentType::escapeIfRequired)
|
||||
.forEach(suggestionsBuilder::suggest);
|
||||
return suggestionsBuilder.buildFuture();
|
||||
}))
|
||||
.executes(commandContext -> {
|
||||
if (!(commandContext.getSource() instanceof Player player)) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Only players can use this command.</red>"));
|
||||
return 1;
|
||||
}
|
||||
Optional<VoteToMuteStarter> instance = VoteToMuteStarter.getInstance(player.getUniqueId());
|
||||
if (instance.isEmpty()) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>You don't have an active vote to mute.</red>"));
|
||||
return 1;
|
||||
}
|
||||
int pageNumber = commandContext.getArgument("page number", Integer.class);
|
||||
instance.get().showPage(pageNumber);
|
||||
return 1;
|
||||
})
|
||||
).executes(commandContext -> {
|
||||
sendHelpMessage(commandContext.getSource());
|
||||
return 1;
|
||||
});
|
||||
|
||||
LiteralArgumentBuilder<CommandSource> enterMessagesNode = LiteralArgumentBuilder
|
||||
.<CommandSource>literal("messages")
|
||||
.requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute"))
|
||||
.then(RequiredArgumentBuilder.<CommandSource, String>argument("list of messages", StringArgumentType.greedyString())
|
||||
.executes(commandContext -> {
|
||||
if (!(commandContext.getSource() instanceof Player player)) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Only players can use this command.</red>"));
|
||||
return 1;
|
||||
}
|
||||
Optional<VoteToMuteStarter> instance = VoteToMuteStarter.getInstance(player.getUniqueId());
|
||||
if (instance.isEmpty()) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>You don't have an active vote to mute.</red>"));
|
||||
return 1;
|
||||
}
|
||||
String listOfPages = commandContext.getArgument("list of messages", String.class);
|
||||
if (!listOfPages.matches("([1-9][0-9]*, )*[1-9][0-9]*")) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Please make sure to format the command correctly.</red>"));
|
||||
return 1;
|
||||
}
|
||||
VoteToMuteStarter voteToMuteStarter = instance.get();
|
||||
|
||||
List<Integer> collect = Arrays.stream(listOfPages.split(", "))
|
||||
.map(Integer::parseInt)
|
||||
.collect(Collectors.toList());
|
||||
Optional<Integer> max = collect.stream().max(Integer::compare);
|
||||
if (max.isEmpty()) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Some of your selected messages do not exist.</red>"));
|
||||
return 1;
|
||||
}
|
||||
int highestLogEntry = max.get();
|
||||
|
||||
if (voteToMuteStarter.getTotalLogEntries() < highestLogEntry) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage("<red>Some of your selected messages do not exist.</red>"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isEmpty()) {
|
||||
sendHelpMessage(commandContext.getSource());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Component chatLogs = voteToMuteStarter.getChatLogsAndClose(collect);
|
||||
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)
|
||||
.start();
|
||||
return 1;
|
||||
})
|
||||
).executes(commandContext -> {
|
||||
sendHelpMessage(commandContext.getSource());
|
||||
return 1;
|
||||
});
|
||||
|
||||
LiteralArgumentBuilder<CommandSource> voteNode = LiteralArgumentBuilder
|
||||
.<CommandSource>literal("vote")
|
||||
.then(playerNode
|
||||
.then(yesNoNode
|
||||
.executes(commandContext -> {
|
||||
if (!(commandContext.getSource() instanceof Player player)) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||
"<red>Only players are allowed to vote</red>"));
|
||||
return 1;
|
||||
}
|
||||
String playerName = commandContext.getArgument("player", String.class);
|
||||
Optional<ActiveVoteToMute> optionalActiveVoteToMute = ActiveVoteToMute.getInstance(playerName);
|
||||
if (optionalActiveVoteToMute.isEmpty()) {
|
||||
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||
"<red>This player does not have an active vote to mute them.</red>"));
|
||||
return 1;
|
||||
}
|
||||
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>"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
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>"));
|
||||
}
|
||||
default -> commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||
"<red><vote> is not a valid vote option</red>", Placeholder.parsed("vote", vote)));
|
||||
}
|
||||
return 1;
|
||||
})).executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
})).executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
});
|
||||
|
||||
LiteralCommandNode<CommandSource> command = LiteralArgumentBuilder
|
||||
.<CommandSource>literal("votetomutehelper")
|
||||
.requires(commandSource -> commandSource.hasPermission("chat.backup-vote-to-mute"))
|
||||
.requires(commandSource -> commandSource instanceof Player)
|
||||
.then(voteNode)
|
||||
.then(pageNode)
|
||||
.then(enterMessagesNode)
|
||||
.executes(context -> {
|
||||
sendHelpMessage(context.getSource());
|
||||
return 1;
|
||||
})
|
||||
.build();
|
||||
|
||||
BrigadierCommand brigadierCommand = new BrigadierCommand(command);
|
||||
|
||||
CommandMeta.Builder metaBuilder = proxyServer.getCommandManager().metaBuilder(brigadierCommand);
|
||||
|
||||
CommandMeta meta = metaBuilder.build();
|
||||
|
||||
proxyServer.getCommandManager().register(meta, brigadierCommand);
|
||||
}
|
||||
|
||||
private int getTotalEligiblePlayers(RegisteredServer server, boolean countLowerRanks) {
|
||||
return (int) server.getPlayersConnected().stream()
|
||||
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
|
||||
.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>"));
|
||||
}
|
||||
|
||||
private List<Player> getEligiblePlayers(ProxyServer proxyServer) {
|
||||
return proxyServer.getAllPlayers().stream()
|
||||
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Player> getEligiblePlayers(RegisteredServer registeredServer) {
|
||||
return registeredServer.getPlayersConnected().stream()
|
||||
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
package com.alttd.velocitychat.commands.vote_to_mute;
|
||||
|
||||
import com.alttd.chat.config.Config;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.alttd.proxydiscordlink.DiscordLink;
|
||||
import com.alttd.proxydiscordlink.lib.net.dv8tion.jda.api.EmbedBuilder;
|
||||
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 net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ActiveVoteToMute {
|
||||
|
||||
private static final HashMap<String, ActiveVoteToMute> instances = new HashMap<>();
|
||||
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;
|
||||
private final boolean countLowerRanks;
|
||||
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))
|
||||
return Optional.empty();
|
||||
return Optional.of(instances.get(username));
|
||||
}
|
||||
|
||||
public static void removePotentialVoter(Player player, RegisteredServer previousServer) {
|
||||
if (!player.hasPermission("chat.backup-vote-to-mute"))
|
||||
return;
|
||||
if (player.hasPermission("chat.vote-to-mute")) {
|
||||
instances.values().stream()
|
||||
.filter(activeVoteToMute -> previousServer == null || activeVoteToMute.getServer().getServerInfo().hashCode() == previousServer.getServerInfo().hashCode())
|
||||
.forEach(inst -> inst.removeEligibleVoter(player.getUniqueId()));
|
||||
} else {
|
||||
instances.values().stream()
|
||||
.filter(ActiveVoteToMute::countLowerRanks)
|
||||
.filter(activeVoteToMute -> previousServer == null || activeVoteToMute.getServer().getServerInfo().hashCode() == previousServer.getServerInfo().hashCode())
|
||||
.forEach(inst -> inst.removeEligibleVoter(player.getUniqueId()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void addPotentialVoter(Player player, ServerConnection server) {
|
||||
if (!player.hasPermission("chat.backup-vote-to-mute"))
|
||||
return;
|
||||
if (player.hasPermission("chat.vote-to-mute")) {
|
||||
instances.values().stream()
|
||||
.filter(activeVoteToMute -> activeVoteToMute.getServer().getServerInfo().hashCode() == server.getServerInfo().hashCode())
|
||||
.forEach(activeVoteToMute -> activeVoteToMute.addEligibleVoter(player));
|
||||
} else {
|
||||
instances.values().stream()
|
||||
.filter(ActiveVoteToMute::countLowerRanks)
|
||||
.filter(activeVoteToMute -> activeVoteToMute.getServer().getServerInfo().hashCode() == server.getServerInfo().hashCode())
|
||||
.forEach(activeVoteToMute -> activeVoteToMute.addEligibleVoter(player));
|
||||
}
|
||||
}
|
||||
|
||||
public ActiveVoteToMute(@NotNull Player votedPlayer, @NotNull RegisteredServer server, ProxyServer proxyServer, Duration duration,
|
||||
int totalEligibleVoters, boolean countLowerRanks, Component chatLogs, @NotNull Player startedByPlayer) {
|
||||
this.chatLogs = chatLogs;
|
||||
this.votedPlayer = votedPlayer;
|
||||
this.totalEligibleVoters = totalEligibleVoters;
|
||||
this.countLowerRanks = countLowerRanks;
|
||||
this.server = server;
|
||||
this.proxyServer = proxyServer;
|
||||
instances.put(votedPlayer.getUsername(), this);
|
||||
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
executorService.schedule(this::endVote,
|
||||
duration.toMinutes(), TimeUnit.MINUTES);
|
||||
this.startedByPlayer = startedByPlayer;
|
||||
}
|
||||
|
||||
private RegisteredServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
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>",
|
||||
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"))
|
||||
.forEach(player -> player.sendMessage(message));
|
||||
}
|
||||
|
||||
public void start() {
|
||||
Component message = getVoteStartMessage();
|
||||
server.getPlayersConnected().stream()
|
||||
.filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute"))
|
||||
.forEach(player -> player.sendMessage(message));
|
||||
}
|
||||
|
||||
public void vote(UUID uuid, boolean votedToMute) {
|
||||
if (votedToMute) {
|
||||
votedFor.add(uuid);
|
||||
votedAgainst.remove(uuid);
|
||||
if (!votePassed()) {
|
||||
return;
|
||||
}
|
||||
endedVote = true;
|
||||
instances.remove(votedPlayer.getUsername());
|
||||
mutePlayer();
|
||||
} else {
|
||||
votedAgainst.add(uuid);
|
||||
votedFor.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean votePassed() {
|
||||
double totalVotes = (votedFor.size() + votedAgainst.size());
|
||||
if (totalVotes == 0 || votedFor.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (totalVotes / totalEligibleVoters < 0.6) {
|
||||
return false;
|
||||
}
|
||||
return votedFor.size() / totalVotes > 0.6;
|
||||
}
|
||||
|
||||
public boolean countLowerRanks() {
|
||||
return countLowerRanks;
|
||||
}
|
||||
|
||||
private void mutePlayer() {
|
||||
Component message = Utility.parseMiniMessage("<prefix> <green>The vote to mute <player> has passed, they will 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"))
|
||||
.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 chatLogsString = PlainTextComponentSerializer.plainText().serialize(chatLogs);
|
||||
EmbedBuilder embedBuilder = buildMutedEmbed(chatLogsString);
|
||||
|
||||
ALogger.info(String.format("Player %s muted by vote\nLogs:\n%s\n\nVotes for:\n%s\nVotes against:\n%s\n",
|
||||
votedPlayer.getUsername(),
|
||||
chatLogsString,
|
||||
parseUUIDsToPlayerOrString(votedFor),
|
||||
parseUUIDsToPlayerOrString(votedAgainst)
|
||||
));
|
||||
|
||||
long id = Config.serverChannelId.get("general");
|
||||
DiscordLink.getPlugin().getBot().sendEmbedToDiscord(id, embedBuilder, -1);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private EmbedBuilder buildMutedEmbed(String chatLogsString) {
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setAuthor(votedPlayer.getUsername(), null, "https://crafatar.com/avatars/" + votedPlayer.getUniqueId() + "?overlay");
|
||||
embedBuilder.setTitle("Player muted by vote");
|
||||
embedBuilder.setColor(Color.CYAN);
|
||||
embedBuilder.addField("Logs",
|
||||
chatLogsString.substring(0, Math.min(chatLogsString.length(), 1024)),
|
||||
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);
|
||||
return embedBuilder;
|
||||
}
|
||||
|
||||
private String parseUUIDsToPlayerOrString(Collection<UUID> uuids) {
|
||||
return uuids.stream().map(uuid -> {
|
||||
Optional<Player> player = proxyServer.getPlayer(uuid);
|
||||
if (player.isPresent()) {
|
||||
return player.get().getUsername();
|
||||
}
|
||||
return uuid.toString();
|
||||
}).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public void addEligibleVoter(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
if (votedAgainst.contains(uuid) || votedFor.contains(uuid))
|
||||
return;
|
||||
totalEligibleVoters++;
|
||||
player.sendMessage(getVoteStartMessage());
|
||||
}
|
||||
|
||||
public void removeEligibleVoter(UUID uuid) {
|
||||
if (votedFor.contains(uuid) || votedAgainst.contains(uuid))
|
||||
return;
|
||||
totalEligibleVoters--;
|
||||
}
|
||||
|
||||
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>
|
||||
<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()),
|
||||
Placeholder.component("prefix", prefix),
|
||||
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;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package com.alttd.velocitychat.commands.vote_to_mute;
|
||||
|
||||
import com.alttd.chat.objects.chat_log.ChatLog;
|
||||
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class VoteToMuteStarter {
|
||||
|
||||
private static final HashMap<UUID, VoteToMuteStarter> instanceMap = new HashMap<>();
|
||||
|
||||
private static final Component prefix = Utility.parseMiniMessage("<gold>[VoteMute]</gold>");
|
||||
private final ChatLogHandler chatLogHandler;
|
||||
private final Player votedPlayer;
|
||||
private final Player commandSource;
|
||||
private final String serverName;
|
||||
private List<Component> parsedChatLogs;
|
||||
private final boolean countLowerRanks;
|
||||
|
||||
public static Optional<VoteToMuteStarter> getInstance(UUID uuid) {
|
||||
if (!instanceMap.containsKey(uuid))
|
||||
return Optional.empty();
|
||||
return Optional.of(instanceMap.get(uuid));
|
||||
}
|
||||
|
||||
public VoteToMuteStarter(ChatLogHandler chatLogHandler, Player votedPlayer, Player commandSource, String serverName, boolean countLowerRanks) {
|
||||
this.chatLogHandler = chatLogHandler;
|
||||
this.votedPlayer = votedPlayer;
|
||||
this.commandSource = commandSource;
|
||||
this.serverName = serverName;
|
||||
this.countLowerRanks = countLowerRanks;
|
||||
instanceMap.put(commandSource.getUniqueId(), this);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
chatLogHandler.retrieveChatLogs(votedPlayer.getUniqueId(), Duration.ofMinutes(10), 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),
|
||||
Placeholder.parsed("player", votedPlayer.getUsername())));
|
||||
return;
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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());
|
||||
parsedChatLogs = IntStream.range(0, chatLogs.size())
|
||||
.mapToObj(i -> Utility.parseMiniMessage(
|
||||
"<number>. <prefix> <player>: <message>",
|
||||
TagResolver.resolver(
|
||||
Placeholder.unparsed("message", chatLogs.get(i).getMessage()),
|
||||
Placeholder.parsed("number", String.valueOf(i + 1)),
|
||||
playerTag, prefixTag
|
||||
))
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void showPage(int page) {
|
||||
List<Component> collect = parsedChatLogs.stream().skip((page - 1) * 10L).limit(10L).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 ")
|
||||
.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 ")
|
||||
.append(page + 1)
|
||||
.append("><hover:show_text:'<gold>Click to go to next page'><gold><next page></gold></hover></click> ");
|
||||
}
|
||||
commandSource.sendMessage(Utility.parseMiniMessage(stringBuilder.toString(),
|
||||
Placeholder.parsed("player", votedPlayer.getUsername()),
|
||||
Placeholder.component("prefix", prefix),
|
||||
Placeholder.component("logs", chatLogsComponent)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the chat logs for the given list of IDs. It removes 1 from the IDs before using them
|
||||
* It removes the instance from the hashmap after this function call
|
||||
*
|
||||
* @param ids A list of integers representing the IDs of the chat logs to retrieve.
|
||||
* @return A Component object containing the selected chat logs joined by newlines.
|
||||
*/
|
||||
public Component getChatLogsAndClose(List<Integer> ids) {
|
||||
List<Component> selectedChatLogs = ids.stream()
|
||||
.filter(id -> id >= 1 && id <= parsedChatLogs.size())
|
||||
.map(id -> parsedChatLogs.get(id - 1))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
instanceMap.remove(commandSource.getUniqueId());
|
||||
return Component.join(JoinConfiguration.newlines(), selectedChatLogs);
|
||||
}
|
||||
|
||||
public int getTotalPages() {
|
||||
return (int) Math.ceil((double) parsedChatLogs.size() / 10);
|
||||
}
|
||||
|
||||
public Player getVotedPlayer() {
|
||||
return votedPlayer;
|
||||
}
|
||||
|
||||
public int getTotalLogEntries() {
|
||||
return parsedChatLogs.size();
|
||||
}
|
||||
|
||||
public boolean countLowerRanks() {
|
||||
return countLowerRanks;
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,14 @@ import com.alttd.chat.managers.ChatUserManager;
|
||||
import com.alttd.chat.objects.ChatUser;
|
||||
import com.alttd.chat.objects.channels.CustomChannel;
|
||||
import com.alttd.chat.util.ALogger;
|
||||
import com.alttd.proxydiscordlink.DiscordLink;
|
||||
import com.alttd.proxydiscordlink.lib.net.dv8tion.jda.api.EmbedBuilder;
|
||||
import com.alttd.velocitychat.VelocityChat;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
@@ -16,8 +19,10 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PluginMessageListener {
|
||||
@@ -93,6 +98,20 @@ public class PluginMessageListener {
|
||||
proxy.getAllServers().forEach(registeredServer ->
|
||||
registeredServer.sendPluginMessage(VelocityChat.getPlugin().getChannelIdentifier(), event.getData()));
|
||||
}
|
||||
case "punish" -> {
|
||||
String playerName = in.readUTF();
|
||||
ProxyServer proxy = VelocityChat.getPlugin().getProxy();
|
||||
ConsoleCommandSource consoleCommandSource = proxy.getConsoleCommandSource();
|
||||
proxy.getCommandManager().executeAsync(consoleCommandSource, String.format("ban %s Automatic ban, please appeal if you feel review is needed.", playerName));
|
||||
ALogger.info(String.format("Auto banned %s due to violating the `punish` filter.", playerName));
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setTitle("Automatic ban through the chat filter");
|
||||
embedBuilder.setAuthor(playerName, null, "https://crafatar.com/avatars/" + in.readUTF() + "?overlay");
|
||||
embedBuilder.setDescription(String.format("`%s`\n\n Auto permanent ban\n\n Auto banned for violating the `punish` chat filter. This could be a false positive! Their message was\n||%s||", playerName, in.readUTF()));
|
||||
embedBuilder.setColor(Color.RED);
|
||||
DiscordLink.getPlugin().getBot().sendEmbedToDiscord(514922317923614728L, embedBuilder, -1);
|
||||
}
|
||||
default -> {
|
||||
VelocityChat.getPlugin().getLogger().info("server " + event.getSource());
|
||||
ProxyServer proxy = VelocityChat.getPlugin().getProxy();
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.alttd.chat.objects.Mail;
|
||||
import com.alttd.chat.util.Utility;
|
||||
import com.alttd.velocitychat.VelocityChat;
|
||||
import com.alttd.chat.config.Config;
|
||||
import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute;
|
||||
import com.alttd.velocitychat.data.ServerWrapper;
|
||||
import com.alttd.velocitychat.handlers.ServerHandler;
|
||||
import com.alttd.chat.managers.PartyManager;
|
||||
@@ -17,6 +18,7 @@ import com.velocitypowered.api.event.connection.LoginEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
@@ -48,9 +50,20 @@ public class ProxyPlayerListener {
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void afterPlayerLogin(ServerPostConnectEvent event) {
|
||||
if (event.getPreviousServer() != null)
|
||||
return;
|
||||
Player player = event.getPlayer();
|
||||
RegisteredServer previousServer = event.getPreviousServer();
|
||||
if (previousServer != null) {
|
||||
ActiveVoteToMute.removePotentialVoter(player, previousServer);
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isEmpty())
|
||||
return;
|
||||
ActiveVoteToMute.addPotentialVoter(player, currentServer.get());
|
||||
return;
|
||||
}
|
||||
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||
if (currentServer.isEmpty())
|
||||
return;
|
||||
ActiveVoteToMute.addPotentialVoter(player, currentServer.get());
|
||||
ChatUser chatUser = ChatUserManager.getChatUser(player.getUniqueId());
|
||||
List<Mail> unReadMail = chatUser.getUnReadMail();
|
||||
if (unReadMail.isEmpty())
|
||||
@@ -62,6 +75,7 @@ public class ProxyPlayerListener {
|
||||
|
||||
@Subscribe
|
||||
public void quitEvent(DisconnectEvent event) {
|
||||
ActiveVoteToMute.removePotentialVoter(event.getPlayer(), null);
|
||||
UUID uuid = event.getPlayer().getUniqueId();
|
||||
Party party = PartyManager.getParty(event.getPlayer().getUniqueId());
|
||||
if (party == null) return;
|
||||
|
||||
Reference in New Issue
Block a user