Skip to content

Commit

Permalink
fix: chat-filter parsing in-game & discord
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Oct 12, 2024
1 parent f80dd1b commit b46d71c
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import com.charleskorn.kaml.YamlComment
import com.mineinabyss.chatty.components.ChannelType
import com.mineinabyss.idofront.serialization.DurationSerializer
import com.mineinabyss.idofront.textcomponents.miniMsg
import kotlinx.serialization.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes

Expand All @@ -29,7 +31,7 @@ data class ChattyConfig(
val commandSpyFormat: String = "<gold><chatty_nickname>: ",
@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<String> = listOf(),
@SerialName("filters") private val _filters: List<String> = listOf(),
val formatURLs: Boolean = true,
val urlReplacements: Set<UrlReplacements> = setOf(
UrlReplacements("^https:\\/\\/cdn\\.discordapp\\.com\\/attachments\\/[^\\/]+\\/[^\\/]+\\.png\\?.*\$", "[Discord Image]"),
Expand All @@ -47,8 +49,7 @@ data class ChattyConfig(
enum class FilterFormat {
STRIKETHROUGH, CENSOR, DELETE, BLOCK
}
@Transient
val filters: List<@Contextual Regex> = _filters.map { it.toRegex() }
@Transient val filters: List<Regex> = _filters.map { it.toRegex() }
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import com.mineinabyss.chatty.components.chattyNickname
import com.mineinabyss.chatty.placeholders.chattyPlaceholderTags
import com.mineinabyss.chatty.tags.ChattyTags
import com.mineinabyss.geary.papermc.tracking.entities.toGearyOrNull
import com.mineinabyss.idofront.font.Space
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.mineinabyss.idofront.textcomponents.serialize
import me.clip.placeholderapi.PlaceholderAPI
import net.kyori.adventure.audience.Audience
import net.kyori.adventure.chat.SignedMessage
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.TextComponent
import net.kyori.adventure.text.TextReplacementConfig
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.event.HoverEvent
Expand Down Expand Up @@ -176,11 +178,11 @@ fun formatPlayerPingMessage(source: Player, pingedPlayer: Player?, audience: Aud
} ?: message
}

fun handleChatFilters(message: Component, player: Player, audience: Player?) : Component? {
fun handleChatFilters(message: Component, player: Player?, audience: Player?) : Component? {
var finalMessage = message
val serialized = finalMessage.serialize()
val serialized = finalMessage.asFlatTextContent()
val filterFormat = chatty.config.chat.filterFormat
if (player.hasPermission(ChattyPermissions.BYPASS_CHAT_FILTERS_PERM)) return finalMessage
if (player?.hasPermission(ChattyPermissions.BYPASS_CHAT_FILTERS_PERM) == true) return finalMessage

val matchResults = chatty.config.chat.filters.flatMap { filter -> filter.findAll(serialized) }
val blockedWords = matchResults.joinToString(", ") { it.value }
Expand All @@ -190,17 +192,17 @@ fun handleChatFilters(message: Component, player: Player, audience: Player?) : C
.replacement(Component.textOfChildren(
when (filterFormat) {
FilterFormat.STRIKETHROUGH -> Component.text(match.value).style(Style.style(TextDecoration.STRIKETHROUGH))
FilterFormat.CENSOR -> Component.text("*".repeat(match.value.length))
FilterFormat.CENSOR -> Component.text("".repeat(match.value.length))
FilterFormat.DELETE -> Component.empty()
FilterFormat.BLOCK -> {
player.sendFormattedMessage(chatty.messages.chatFilter.blockMessage, blockedWords)
player?.sendFormattedMessage(chatty.messages.chatFilter.blockMessage, blockedWords)
if (audience?.hasPermission(ChattyPermissions.MODERATION_PERM) == true)
audience.sendFormattedMessage(chatty.messages.chatFilter.notifyStaff + blockedWords)
return null
}
}.takeIf { it != Component.empty() }?.let {
if (audience?.hasPermission(ChattyPermissions.MODERATION_PERM) == true)
it.hoverEventShowText(Component.text(match.value).style(Style.style(TextDecoration.ITALIC)))
it.hoverEventShowText(Component.text(match.value.toCharArray().joinToString(Space.PLUS_1.unicode)).style(Style.style(TextDecoration.ITALIC)))
else it
} ?: Component.empty()
))
Expand All @@ -210,7 +212,7 @@ fun handleChatFilters(message: Component, player: Player, audience: Player?) : C
// If filterFormat is DELETE and message is empty, aka only containing blocked words
// Give feedback to player and notify staff
if (finalMessage == Component.empty() && filterFormat == FilterFormat.DELETE) {
if (audience == player) player.sendFormattedMessage(chatty.messages.chatFilter.deleteWordsEmptyMessage)
if (audience == player) player?.sendFormattedMessage(chatty.messages.chatFilter.deleteWordsEmptyMessage)
else if (audience?.hasPermission(ChattyPermissions.MODERATION_PERM) == true)
audience.sendFormattedMessage(chatty.messages.chatFilter.notifyStaff, blockedWords, optionalPlayer = player)
}
Expand All @@ -219,6 +221,7 @@ fun handleChatFilters(message: Component, player: Player, audience: Player?) : C
}

fun handleUrlReplacements(message: Component, player: Player?): Component {
if (!chatty.config.chat.formatURLs) return message
var component = message
component.clickEvent()?.takeIf { it.action() == ClickEvent.Action.OPEN_URL }?.let { clickEvent ->
val (regex, replacement) = chatty.config.chat.urlReplacements.firstOrNull { it.regex in clickEvent.value() }?.let { it.regex to it.replacement } ?: return@let
Expand All @@ -227,4 +230,11 @@ fun handleUrlReplacements(message: Component, player: Player?): Component {
}

return component.children(component.children().map { handleUrlReplacements(it, player) })
}

fun Component.asFlatTextContent(): String {
return buildString {
append((this@asFlatTextContent as? TextComponent)?.content())
append(this@asFlatTextContent.children().joinToString("") { it.asFlatTextContent() })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,22 @@ class DiscordListener {
val channel = defaultChannel().takeIf { it.value.discordsrv } ?: globalChannel()?.takeIf { it.value.discordsrv } ?: chatty.config.channels.entries.firstOrNull { it.value.discordsrv } ?: return
val channelId = ComponentDSV.text(channel.key)
val simpleMessage = mm.deserialize(message.author.name + ": " + message.contentRaw)
val message = if (chatty.config.chat.formatURLs) handleUrlReplacements(minecraftMessage.toComponent(), null).toComponentDSV() else minecraftMessage
val minecraftMessage = ComponentDSV.textOfChildren(senderName, channelId, message, simpleMessage)
var message = handleUrlReplacements(minecraftMessage.toComponent(), null)
message = handleChatFilters(message, null, null) ?: run { isCancelled = true; return }

val minecraftMessage = ComponentDSV.textOfChildren(senderName, channelId, message.toComponentDSV(), simpleMessage)
chatty.plugin.server.sendPluginMessage(chatty.plugin, discordSrvChannel, gson.serialize(minecraftMessage).toByteArray())
setMinecraftMessage(message)
setMinecraftMessage(message.toComponentDSV())
}

private fun ComponentDSV.toComponent() = mm.serialize(this).miniMsg()
private fun Component.toComponentDSV() = mm.deserialize(this.serialize())

@Subscribe(priority = ListenerPriority.NORMAL)
fun GameChatMessagePreProcessEvent.onChat() {
val channel = player.toGeary().get<ChannelData>()?.channel ?: return
val channel = player.toGeary().get<ChannelData>()?.withChannelVerified()?.channel ?: return
val baseMessage = messageComponent.children().last().toComponent()
val filteredMessage = handleChatFilters(baseMessage, player, null)?.toComponentDSV()

if (!channel.discordsrv || filteredMessage == null) isCancelled = true
else messageComponent = filteredMessage
}
Expand Down

0 comments on commit b46d71c

Please sign in to comment.