From c3c7a9f2ab55409fe0c9e5299eb1346b55b1d107 Mon Sep 17 00:00:00 2001 From: Boy Date: Wed, 10 Jan 2024 23:14:22 +0100 Subject: [PATCH] feat: add chat-filters --- .../com/mineinabyss/chatty/ChattyChannel.kt | 6 ++-- .../com/mineinabyss/chatty/ChattyConfig.kt | 14 ++++++-- .../mineinabyss/chatty/helpers/ChatHelpers.kt | 32 ++++++++++++++++++ .../chatty/helpers/ChattyPermissions.kt | 1 + .../chatty/listeners/ChatListener.kt | 33 ++++++++++--------- 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt index f1d1bc5..fa5549e 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt @@ -4,6 +4,7 @@ import com.mineinabyss.chatty.components.ChannelType import com.mineinabyss.chatty.components.SpyOnChannels import com.mineinabyss.chatty.queries.SpyingPlayers import com.mineinabyss.geary.papermc.tracking.entities.toGeary +import com.mineinabyss.idofront.textcomponents.miniMsg import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.kyori.adventure.audience.Audience @@ -40,8 +41,9 @@ data class ChattyChannel( } } val key by lazy { chatty.config.channels.entries.first { it.value == this }.key } - val messageColor: TextColor? - get() = _messageColor?.let { TextColor.fromHexString(_messageColor) ?: NamedTextColor.NAMES.value(_messageColor) } + val messageColor: TextColor? get() = _messageColor?.let { + TextColor.fromHexString(_messageColor) ?: NamedTextColor.NAMES.value(_messageColor) ?: ("<$_messageColor>").miniMsg().color() + } fun getAudience(player: Player): Collection { diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt index dfe9c77..0503f60 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt @@ -1,8 +1,9 @@ package com.mineinabyss.chatty +import com.charleskorn.kaml.YamlComment import com.mineinabyss.chatty.components.ChannelType import com.mineinabyss.idofront.serialization.DurationSerializer -import kotlinx.serialization.Serializable +import kotlinx.serialization.* import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes @@ -25,7 +26,16 @@ data class ChattyConfig( data class Chat( val disableChatSigning: Boolean = true, val commandSpyFormat: String = ": ", - ) + @YamlComment("Valid formats: STRIKETHROUGH, CENSOR, DELETE, BLOCK", "STRIKETHROUGH: Replaces filtered words with a strikethrough", "CENSOR: Replaces filtered words with a censor", "DELETE: Deletes filtered words", "BLOCK: Blocks filtered words from being sent") + val filterFormat: FilterFormat = FilterFormat.CENSOR, + @SerialName("filters") val _filters: List = listOf(), + ) { + enum class FilterFormat { + STRIKETHROUGH, CENSOR, DELETE, BLOCK + } + @Transient + val filters: List<@Contextual Regex> = _filters.map { it.toRegex() } + } @Serializable data class PrivateMessages( diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt index 81a3159..616a96a 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt @@ -4,11 +4,13 @@ import com.combimagnetron.imageloader.Avatar import com.combimagnetron.imageloader.Image.ColorType import com.combimagnetron.imageloader.ImageUtils import com.mineinabyss.chatty.ChattyChannel +import com.mineinabyss.chatty.ChattyConfig import com.mineinabyss.chatty.chatty import com.mineinabyss.chatty.components.ChannelData import com.mineinabyss.chatty.components.ChannelType import com.mineinabyss.chatty.components.chattyNickname import com.mineinabyss.chatty.placeholders.chattyPlaceholderTags +import com.mineinabyss.idofront.messaging.warn import com.mineinabyss.idofront.textcomponents.miniMsg import com.mineinabyss.idofront.textcomponents.serialize import me.clip.placeholderapi.PlaceholderAPI @@ -270,4 +272,34 @@ fun formatPlayerPingMessage(source: Player, pingedPlayer: Player?, audience: Aud .build() ) } ?: message +} + +fun handleChatFilters(message: Component, player: Player, audience: Player) : Component { + var finalMessage = message + val serialized = finalMessage.serialize() + if (!player.hasPermission(ChattyPermissions.BYPASS_CHAT_FILTERS_PERM)) chatty.config.chat.filters.forEach { filter -> + filter.findAll(serialized).forEach { match -> + finalMessage = finalMessage.replaceText(TextReplacementConfig.builder() + .matchLiteral(match.value) + .replacement(Component.textOfChildren( + when (chatty.config.chat.filterFormat) { + ChattyConfig.Chat.FilterFormat.STRIKETHROUGH -> Component.text(match.value).style(Style.style(TextDecoration.STRIKETHROUGH)) + ChattyConfig.Chat.FilterFormat.CENSOR -> Component.text("*".repeat(match.value.length)) + ChattyConfig.Chat.FilterFormat.DELETE -> Component.empty() + ChattyConfig.Chat.FilterFormat.BLOCK -> { + player.warn("Your message contained a blocked word: ${match.value}") + if (audience.hasPermission(ChattyPermissions.MODERATION_PERM)) + audience.sendFormattedMessage("Player sent a blocked message: ${match.value}") + return Component.empty() + } + }.let { + if (audience.hasPermission(ChattyPermissions.MODERATION_PERM)) + it.hoverEventShowText(Component.text(match.value).style(Style.style(TextDecoration.ITALIC))) + else it + } + )) + .build()) + } + } + return finalMessage } \ No newline at end of file diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChattyPermissions.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChattyPermissions.kt index 9111aa4..b20e941 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChattyPermissions.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChattyPermissions.kt @@ -9,6 +9,7 @@ object ChattyPermissions { const val NICKNAME = "chatty.nickname" const val NICKNAME_OTHERS = "chatty.nickname.others" const val BYPASS_TAG_PERM = "chatty.tags.bypass" + const val BYPASS_CHAT_FILTERS_PERM = "chatty.chat.filters.bypass" const val MODERATION_PERM = "chatty.moderation" val chatFormattingPerms = mapOf( Permission("chatty.tags.color") to StandardTags.color(), diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt index 8d17517..a09c4dc 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt @@ -73,11 +73,12 @@ class ChatListener : Listener { val pingedPlayer = originalMessage().serialize().checkForPlayerPings(channelId) val playerViewers = viewers().filterIsInstance().toSet() + var finalMessage = message() when { viewers().isEmpty() -> player.sendFormattedMessage(chatty.messages.channels.emptyChannelMessage) chatty.config.chat.disableChatSigning -> { playerViewers.forEach { receiver -> - var finalMessage = message() + finalMessage = handleChatFilters(finalMessage, player, receiver) finalMessage = appendChannelFormat(finalMessage, player, channel) finalMessage = formatPlayerPingMessage(player, pingedPlayer, receiver, finalMessage) finalMessage = formatModerationMessage( @@ -97,21 +98,23 @@ class ChatListener : Listener { //isCancelled = true } - else -> renderer { source, _, message, audience -> - var finalMessage = message + else -> { finalMessage = appendChannelFormat(finalMessage, player, channel) - finalMessage = formatPlayerPingMessage(source, pingedPlayer, audience, finalMessage) - finalMessage = formatModerationMessage( - channel.messageDeletion, - finalMessage, - simpleMessage, - signedMessage(), - audience, - source, - playerViewers - ) - - return@renderer finalMessage + renderer { source, _, message, audience -> + finalMessage = handleChatFilters(finalMessage, player, audience as? Player ?: player) + finalMessage = formatPlayerPingMessage(source, pingedPlayer, audience, finalMessage) + finalMessage = formatModerationMessage( + channel.messageDeletion, + finalMessage, + simpleMessage, + signedMessage(), + audience, + source, + playerViewers + ) + + return@renderer finalMessage + } } } }