diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyMessages.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyMessages.kt index 173c95f..bddcc3f 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyMessages.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyMessages.kt @@ -45,7 +45,7 @@ object ChattyMessages : IdofrontConfig(chatty, Messages val otherEmpty: String = "Removed nickname for %player_name%!", val invalidPlayer: String = "That player doesn't exist!", val consoleNicknameSelf: String = "Sadly console cannot have cool nickname :(", - val disallowedStyling: String = "This nickname contains formatting that are not allowed!", + val disallowedStyling: String = "This nickname formatting you cannot use!", val tooLong: String = "The nickname was too long!", ) @@ -82,6 +82,7 @@ object ChattyMessages : IdofrontConfig(chatty, Messages @Serializable data class Other( + val disallowedStyling: String = "Your message contains formatting you cannot use!", val configReloaded: String = "Chatty config reloaded.", val messagesReloaded: String = "Chatty messages reloaded.", val nickNameChanged: String = "Nickname set to &%chatty_player_displayname%.", 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 fbdf09e..f798b41 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,17 +4,16 @@ import com.combimagnetron.imageloader.Image import com.mineinabyss.chatty.components.ChannelType import com.mineinabyss.chatty.components.chattyData import com.mineinabyss.idofront.messaging.miniMsg +import com.mineinabyss.idofront.messaging.serialize import me.clip.placeholderapi.PlaceholderAPI import net.kyori.adventure.key.Key import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextReplacementConfig import net.kyori.adventure.text.format.Style -import net.kyori.adventure.text.minimessage.MiniMessage -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer import org.bukkit.Bukkit import org.bukkit.ChatColor import org.bukkit.Sound +import org.bukkit.SoundCategory import org.bukkit.entity.Player const val ZERO_WIDTH = "\u200B" @@ -41,24 +40,24 @@ fun Component.handlePlayerPings(player: Player, pingedPlayer: Player) { val pingSound = pingedPlayer.chattyData.pingSound ?: ping.defaultPingSound val clickToReply = if (ping.clickToReply) "Shift + Click to mention!'>" else "" val pingMessage = this.replaceText( TextReplacementConfig.builder() - .match(ping.pingPrefix + player.chattyData.displayName) - .replacement((ping.pingReceiveFormat + clickToReply + ping.pingPrefix + player.chattyData.displayName).miniMsg()) + .match(ping.pingPrefix + pingedPlayer.chattyData.displayName) + .replacement((ping.pingReceiveFormat + clickToReply + ping.pingPrefix + pingedPlayer.chattyData.displayName).miniMsg()) .build() ) if (!pingedPlayer.chattyData.disablePingSound) - pingedPlayer.playSound(pingedPlayer.location, pingSound, ping.pingVolume, ping.pingPitch) + pingedPlayer.playSound(pingedPlayer.location, pingSound, SoundCategory.VOICE, ping.pingVolume, ping.pingPitch) pingedPlayer.sendMessage(pingMessage) val pingerMessage = this.replaceText( TextReplacementConfig.builder() - .match(ping.pingPrefix + player.chattyData.displayName) - .replacement((ping.pingSendFormat + clickToReply + ping.pingPrefix + player.chattyData.displayName).miniMsg()) + .match(ping.pingPrefix + pingedPlayer.chattyData.displayName) + .replacement((ping.pingSendFormat + ping.pingPrefix + pingedPlayer.chattyData.displayName).miniMsg()) .build() ) player.sendMessage(pingerMessage) @@ -100,8 +99,8 @@ fun translatePlaceholders(player: Player, message: String): Component { TextReplacementConfig.builder() .match("%chatty_playerhead%") .replacement(player.translatePlayerHeadComponent()).build() - ) - return PlaceholderAPI.setPlaceholders(player, msg.serialize()).deSerializeLegacy() + ).serialize() + return PlaceholderAPI.setPlaceholders(player, msg).miniMsg().fixLegacy() } val playerHeadMapCache = mutableMapOf() @@ -116,7 +115,7 @@ fun Player.translatePlayerHeadComponent(): Component { } private fun convertToImageComponent(image: String, font: Key): Component { - return MiniMessage.builder().build().deserialize(image).style(Style.style().font(font).build()) + return mm.deserialize(image).style(Style.style().font(font).build()) } private fun convertURLToImageString( @@ -125,61 +124,33 @@ private fun convertURLToImageString( return Image.builder().image(url).colorType(colorType).ascent(ascent).build().generate() } -fun String.deSerializeLegacy() = LegacyComponentSerializer.legacy('ยง').deserialize(this).fixLegacy() - fun Component.fixLegacy(): Component = this.serialize().replace("\\<", "<").replace("\\>", ">").miniMsg() -// Splits and tags and checks if they're allowed -fun String.verifyChatStyling(): String { - val finalString = this - this.getTags().filter { tag -> tag !in chattyConfig.chat.allowedTags }.forEach { tag -> - finalString.replace(tag.toString().lowercase(), "\\<${tag.toString().lowercase()}") - } - return finalString -} +fun Component.serialize() = mm.serialize(this) -fun String.verifyBookStyling(): String { - val finalString = this - this.getTags().filter { tag -> tag !in chattyConfig.book.allowedTags }.forEach { tag -> - finalString.replace(tag.toString().lowercase(), "\\<${tag.toString().lowercase()}") +fun Component.toPlainText() = plainText.serialize(this) + +// Cache tagmap so as it is static +private var cachedTags = mutableSetOf() +fun String.getTags(): Set { + val tags = mutableSetOf() + val allTags = cachedTags.takeIf { it.isNotEmpty() } ?: run { + cachedTags += ChattyTags.values().map { t -> "<${t.name.lowercase()}" } + cachedTags += ChatColor.values().map { c -> "<${c.name.lowercase()}" } + cachedTags } - return finalString -} -fun Component.serialize() = MiniMessage.builder().build().serialize(this) - -fun Component.toPlainText() = PlainTextComponentSerializer.builder().build().serialize(this) - -fun Component.stripTags() = MiniMessage.builder().build().stripTags(this.serialize()) - -fun String.getTags(): List { - val tags = mutableListOf() - if (" " in this) tags.add(ChattyTags.SPACES) - MiniMessage.builder().build().deserializeToTree(this).toString() - .split("TagNode(", ") {").filter { "Node" !in it && it.isNotBlank() }.toList().forEach { - val tag = it.replace("'", "").replace(",", "") - when { - tag in ChatColor.values().toString().lowercase() -> tags.add(ChattyTags.TEXTCOLOR) - tag.startsWith("gradient") -> tags.add(ChattyTags.GRADIENT) - tag.startsWith("#") -> tags.add(ChattyTags.HEXCOLOR) - tag.startsWith("i") || tag.startsWith("italic") -> tags.add(ChattyTags.ITALIC) - tag.startsWith("b") || tag.startsWith("bold") -> tags.add(ChattyTags.BOLD) - tag.startsWith("u") || tag.startsWith("underline") -> tags.add(ChattyTags.UNDERLINE) - tag.startsWith("st") || tag.startsWith("strikethrough") -> tags.add(ChattyTags.STRIKETHROUGH) - tag.startsWith("obf") || tag.startsWith("obfuscated") -> tags.add(ChattyTags.OBFUSCATED) - tag.startsWith("click") -> tags.add(ChattyTags.CLICK) - tag.startsWith("hover") -> tags.add(ChattyTags.HOVER) - tag.startsWith("insert") -> tags.add(ChattyTags.INSERTION) - tag.startsWith("rainbow") -> tags.add(ChattyTags.RAINBOW) - tag.startsWith("transition") -> tags.add(ChattyTags.TRANSITION) - tag.startsWith("reset") -> tags.add(ChattyTags.RESET) - tag.startsWith("font") -> tags.add(ChattyTags.FONT) - tag.startsWith("key") -> tags.add(ChattyTags.KEYBIND) - tag.startsWith("lang") -> tags.add(ChattyTags.TRANSLATABLE) - } + allTags.forEach { + val tag = it.replace("<", "") + if (tag.isNotBlank() && it in this) { + if (tag.uppercase() in ChattyTags.values().map { t -> t.name }) + tags += ChattyTags.valueOf(tag.uppercase()) + else if (tag.uppercase() in ChatColor.values().map { c -> c.name }) + tags += ChattyTags.TEXTCOLOR } - return tags.toList() + } + return tags } fun List.toSentence() = this.joinToString(" ") diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/NicknameHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/NicknameHelpers.kt index 2c938e2..cdfddab 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/NicknameHelpers.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/NicknameHelpers.kt @@ -7,7 +7,7 @@ import org.bukkit.entity.Player fun String.verifyNickLength(): Boolean { return when (chattyConfig.nicknames.countColorsInLength) { true -> this.length <= chattyConfig.nicknames.maxLength - false -> this.miniMsg().stripTags().length <= chattyConfig.nicknames.maxLength + false -> this.miniMsg().toPlainText().length <= chattyConfig.nicknames.maxLength } } diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/PluginHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/PluginHelpers.kt index 6fe04f0..b941481 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/PluginHelpers.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/PluginHelpers.kt @@ -2,12 +2,16 @@ package com.mineinabyss.chatty.helpers import com.mineinabyss.chatty.ChattyConfig import com.mineinabyss.chatty.ChattyMessages +import net.kyori.adventure.text.minimessage.MiniMessage +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer import org.bukkit.entity.Player //val bondrewd = BondrewdLikesHisEmotes.getPlugin(BondrewdLikesHisEmotes::class.java) val chattyConfig = ChattyConfig.data val chattyMessages = ChattyMessages.data val emoteFixer = DiscordEmoteFixer.data +val mm = MiniMessage.miniMessage() +val plainText = PlainTextComponentSerializer.plainText() fun Player.checkPermission(perm: String): Boolean { return if (perm.isEmpty()) true 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 8865ccc..fd2b43b 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 @@ -41,10 +41,9 @@ class ChatListener : Listener { val channelId = player.chattyData.channelId val channel = getChannelFromId(channelId) ?: return val formatted = - if (player.checkPermission(chattyConfig.chat.bypassFormatPermission)) originalMessage().fixLegacy() - else originalMessage().serialize().verifyChatStyling().miniMsg() - - result("".miniMsg().append(translatePlaceholders(player, channel.format + formatted.serialize()))) + if (player.checkPermission(chattyConfig.chat.bypassFormatPermission)) originalMessage().fixLegacy().serialize() + else originalMessage().serialize().verifyChatStyling(player) + result("".miniMsg().append(translatePlaceholders(player, channel.format + formatted))) } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @@ -54,13 +53,12 @@ class ChatListener : Listener { val channel = getChannelFromId(channelId) ?: return if (viewers().isNotEmpty()) viewers().clear() - viewers().addAll(setAudienceForChannelType(player)) + viewers() += setAudienceForChannelType(player) val pingedPlayer = originalMessage().serialize().checkForPlayerPings(channelId) if (pingedPlayer != null && pingedPlayer != player && pingedPlayer in viewers()) { message().handlePlayerPings(player, pingedPlayer) - viewers().remove(pingedPlayer) - viewers().remove(player) + viewers() -= setOf(pingedPlayer, player) } if (channel.proxy) { @@ -80,6 +78,14 @@ class ChatListener : Listener { } } + private fun String.verifyChatStyling(player: Player): String { + return if (this.getTags().all { tag -> tag in chattyConfig.chat.allowedTags }) this + else { + player.sendFormattedMessage(chattyMessages.other.disallowedStyling) + mm.stripTags(miniMsg().toPlainText()) + } + } + private fun setAudienceForChannelType(player: Player): Set { val onlinePlayers = Bukkit.getOnlinePlayers() val channel = getChannelFromId(player.chattyData.channelId) ?: return emptySet() diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/DiscordListener.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/DiscordListener.kt index 473cad3..640ba4d 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/DiscordListener.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/DiscordListener.kt @@ -3,8 +3,12 @@ package com.mineinabyss.chatty.listeners import com.mineinabyss.chatty.chatty import com.mineinabyss.chatty.chattyProxyChannel import com.mineinabyss.chatty.components.chattyData -import com.mineinabyss.chatty.helpers.* +import com.mineinabyss.chatty.helpers.emoteFixer +import com.mineinabyss.chatty.helpers.getChannelFromId +import com.mineinabyss.chatty.helpers.getChannelFromPlayer +import com.mineinabyss.chatty.helpers.translatePlayerHeadComponent import com.mineinabyss.idofront.messaging.miniMsg +import com.mineinabyss.idofront.messaging.serialize import github.scarsz.discordsrv.api.ListenerPriority import github.scarsz.discordsrv.api.Subscribe import github.scarsz.discordsrv.api.events.* @@ -15,15 +19,21 @@ import github.scarsz.discordsrv.dependencies.jda.api.entities.MessageEmbed.Field import github.scarsz.discordsrv.dependencies.kyori.adventure.text.Component import github.scarsz.discordsrv.dependencies.kyori.adventure.text.TextReplacementConfig import github.scarsz.discordsrv.dependencies.kyori.adventure.text.minimessage.MiniMessage +import github.scarsz.discordsrv.dependencies.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer import github.scarsz.discordsrv.objects.MessageFormat -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer +import me.clip.placeholderapi.PlaceholderAPI import org.bukkit.Bukkit +import org.bukkit.entity.Player class DiscordListener { + private val mm = MiniMessage.builder().build() + private val plainText = PlainTextComponentSerializer.plainText() + @Subscribe(priority = ListenerPriority.HIGHEST) fun DiscordGuildMessagePostProcessEvent.sendDiscordToProxy() { + minecraftMessage = (minecraftMessage.serialize().substringBefore(message.contentRaw) + mm.stripTokens(message.contentStripped)).miniMessage() Bukkit.getServer().sendPluginMessage(chatty, chattyProxyChannel, minecraftMessage.serialize().toByteArray()) } @@ -36,9 +46,8 @@ class DiscordListener { else if (!channel.discordsrv || (channel != lastUsedChannel && !lastUsedChannel.discordsrv)) isCancelled = true else { - val plain = PlainTextComponentSerializer.builder().build() - val format = plain.serialize(translatePlaceholders(player, player.getChannelFromPlayer()?.format.toString())) - val msg = plain.serialize(messageComponent.serialize().miniMsg()).replace(format, "") + val format = plainText.serialize(translatePlaceholders(player, player.getChannelFromPlayer()?.format.toString())) + val msg = plainText.serialize(messageComponent).replace(format, "") messageComponent = msg.miniMessage().translateEmoteIDsToComponent() } } @@ -62,86 +71,95 @@ class DiscordListener { if (isCancelled) return discordMessage = discordMessage.translatePostFormat() } -} -private fun Message.translatePostFormat(): Message { - val message = this - val embeds = mutableListOf() - val fields = mutableListOf() + private fun translatePlaceholders(player: Player, message: String): Component { + val msg = message.miniMsg().replaceText( + net.kyori.adventure.text.TextReplacementConfig.builder() + .match("%chatty_playerhead%") + .replacement(player.translatePlayerHeadComponent()).build() + ).serialize() + return PlaceholderAPI.setPlaceholders(player, msg).miniMessage().fixLegacy() + } - message.embeds.forEach { embed -> - if (embed.fields.isNotEmpty()) - embed.fields.forEach { field -> - fields.add(Field(field.name?.translateEmoteIDs(), field.value?.translateEmoteIDs(), field.isInline)) - } - embeds.add( - MessageEmbed( - embed.url, embed.title?.translateEmoteIDs(), embed.description?.translateEmoteIDs(), - embed.type, embed.timestamp, embed.colorRaw, embed.thumbnail, embed.siteProvider, - embed.author, embed.videoInfo, embed.footer, embed.image, fields + private fun Component.fixLegacy(): Component = + mm.deserialize(this.serialize().replace("\\<", "<").replace("\\>", ">")) + + private fun Message.translatePostFormat(): Message { + val message = this + val embeds = mutableListOf() + val fields = mutableListOf() + + message.embeds.forEach { embed -> + if (embed.fields.isNotEmpty()) + embed.fields.forEach { field -> + fields.add(Field(field.name?.translateEmoteIDs(), field.value?.translateEmoteIDs(), field.isInline)) + } + embeds.add( + MessageEmbed( + embed.url, embed.title?.translateEmoteIDs(), embed.description?.translateEmoteIDs(), + embed.type, embed.timestamp, embed.colorRaw, embed.thumbnail, embed.siteProvider, + embed.author, embed.videoInfo, embed.footer, embed.image, fields + ) ) - ) + } + var builder = MessageBuilder(message) + if (embeds.isNotEmpty()) builder = builder.setEmbeds(embeds) + return builder.build() } - var builder = MessageBuilder(message) - if (embeds.isNotEmpty()) builder = builder.setEmbeds(embeds) - return builder.build() -} -private fun MessageFormat.translatePreFormat(): MessageFormat { - val fields = mutableListOf() - val format = this - if (content != null) - format.content = content.translateEmoteIDs() - if (title != null) - format.title = title.translateEmoteIDs() - if (description != null) - format.description = description.translateEmoteIDs() - if (format.fields != null) - format.fields.forEach { f -> - fields.add(Field(f.name?.translateEmoteIDs(), f.value?.translateEmoteIDs(), f.isInline)) - } - format.fields = fields - return format -} + private fun MessageFormat.translatePreFormat(): MessageFormat { + val fields = mutableListOf() + val format = this + if (content != null) + format.content = content.translateEmoteIDs() + if (title != null) + format.title = title.translateEmoteIDs() + if (description != null) + format.description = description.translateEmoteIDs() + if (format.fields != null) + format.fields.forEach { f -> + fields.add(Field(f.name?.translateEmoteIDs(), f.value?.translateEmoteIDs(), f.isInline)) + } + format.fields = fields + return format + } -private fun Component.cleanUpHackyFix() = - this.replaceText(TextReplacementConfig.builder().match("<<").replacement("<").build()) + private fun Component.cleanUpHackyFix() = + this.replaceText(TextReplacementConfig.builder().match("<<").replacement("<").build()) -private fun String.cleanUpHackyFix() = - this.deSerializeLegacy().stripTags().replace("\\<", "<").replace("<<", "<") + private fun String.cleanUpHackyFix() = + plainText.serialize(this.miniMessage()).replace("\\<", "<").replace("<<", "<") -private fun String.translateEmoteIDs(): String { - var translated = this - emoteFixer.emotes.entries.forEach { (emoteId, replacement) -> - val id = ":$emoteId:" - if (id in this) { - translated = translated.replace(id, replacement) + private fun String.translateEmoteIDs(): String { + var translated = this + emoteFixer.emotes.entries.forEach { (emoteId, replacement) -> + val id = ":$emoteId:" + if (id in this) { + translated = translated.replace(id, replacement) + } } + return translated.cleanUpHackyFix() } - return translated.cleanUpHackyFix() -} -private fun Component.translateEmoteIDsToComponent(): Component { - var translated = this - emoteFixer.emotes.entries.forEach { (emoteId, replacement) -> - val id = ":$emoteId:" - if (id in translated.serialize()) { - translated = translated.replaceText( - TextReplacementConfig.builder().match(id) - .replacement("<$replacement".miniMessage()).build() - ) + private fun Component.translateEmoteIDsToComponent(): Component { + var translated = this + emoteFixer.emotes.entries.forEach { (emoteId, replacement) -> + val id = ":$emoteId:" + if (id in translated.serialize()) { + translated = translated.replaceText( + TextReplacementConfig.builder().match(id) + .replacement("<$replacement".miniMessage()).build() + ) + } } + return translated.cleanUpHackyFix() + } + private fun Component.serialize(): String { + return mm.serialize(this) } - return translated.cleanUpHackyFix() -} - -private fun Component.serialize(): String { - return MiniMessage.builder().build() - .serialize(this) -} -private fun String.miniMessage(): Component { - return MiniMessage.builder().build() - .deserialize(this) + private fun String.miniMessage(): Component { + return mm.deserialize(this) + } } diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt index 11504e5..0c9550b 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt @@ -86,6 +86,14 @@ class PlayerListener : Listener { } } + private fun String.verifyBookStyling(): String { + val finalString = this + this.getTags().filter { tag -> tag !in chattyConfig.book.allowedTags }.forEach { tag -> + finalString.replace(tag.toString().lowercase(), "\\<${tag.toString().lowercase()}") + } + return finalString + } + private fun String.verifySignStyling(): String { val finalString = this this.getTags().filter { tag -> tag !in chattyConfig.sign.allowedTags }.forEach { tag -> diff --git a/chatty-paper/src/main/resources/messages.yml b/chatty-paper/src/main/resources/messages.yml index d47886d..64fa6d5 100644 --- a/chatty-paper/src/main/resources/messages.yml +++ b/chatty-paper/src/main/resources/messages.yml @@ -41,6 +41,7 @@ joinLeave: leaveMessage: "%player_name% has left the server." other: + disallowedStyling: "Your message contains formatting you cannot use!" configReloaded: "Chatty config reloaded." messagesReloaded: "Chatty messages reloaded." nickNameChanged: "Nickname set to %chatty_player_displayname%."