diff --git a/chatty-paper/build.gradle.kts b/chatty-paper/build.gradle.kts index 0e55598..1488e00 100644 --- a/chatty-paper/build.gradle.kts +++ b/chatty-paper/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { // Shaded implementation(libs.imageloader) + implementation(libs.deepl) } 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 d5fa3ff..cb7c9d2 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyChannel.kt @@ -1,6 +1,8 @@ package com.mineinabyss.chatty +import com.charleskorn.kaml.YamlComment import com.mineinabyss.chatty.components.ChannelType +import com.mineinabyss.chatty.helpers.TranslationLanguage import com.mineinabyss.idofront.textcomponents.miniMsg import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -18,6 +20,7 @@ data class ChattyChannel( val proxy: Boolean = false, val discordsrv: Boolean = true, val messageDeletion: MessageDeletion = MessageDeletion(), + val translation: Translation = Translation(), val isDefaultChannel: Boolean = false, val isStaffChannel: Boolean = false, val format: String = "", @@ -26,6 +29,25 @@ data class ChattyChannel( val channelAliases: List = listOf(), ) { + @Serializable + data class Translation( + @YamlComment("Which type of translation should be done for the target language.", + "FORCE - Forces all messages to be translated to the target language", + "SKIP_SAME_LANGUAGE - Avoids translating a message if the senders language is same as ones own", + "ALL_SAME_LANGUAGE - Translates all messages to the receivers language", + "NONE - Disables translation" + ) + val type: TargetLanguageType = TargetLanguageType.NONE, + @YamlComment("The default language for this channel to translate to.") + val targetLanguage: TranslationLanguage = TranslationLanguage.English_US, + //@YamlComment("Whether there should be a rate limitation per player for this channel.") + //val rateLimitPerPlayer: Boolean = true, + ) { + enum class TargetLanguageType { + FORCE, SKIP_SAME_LANGUAGE, ALL_SAME_LANGUAGE, NONE + } + } + @Serializable data class MessageDeletion( val enabled: Boolean = false, 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 4131b22..064d4ab 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyConfig.kt @@ -1,7 +1,10 @@ package com.mineinabyss.chatty import com.charleskorn.kaml.YamlComment +import com.deepl.api.Language +import com.deepl.api.LanguageCode import com.mineinabyss.chatty.components.ChannelType +import com.mineinabyss.chatty.helpers.TranslationLanguage import com.mineinabyss.idofront.serialization.DurationSerializer import com.mineinabyss.idofront.textcomponents.miniMsg import kotlinx.serialization.* @@ -10,6 +13,7 @@ import kotlin.time.Duration.Companion.minutes @Serializable data class ChattyConfig( + val translation: Translation = Translation(), val playerHeadFont: String = "minecraft:chatty_heads", val nicknames: Nickname = Nickname(), val chat: Chat = Chat(), @@ -23,6 +27,16 @@ data class ChattyConfig( val channels: MutableMap = mutableMapOf("global" to ChattyChannel(ChannelType.GLOBAL, messageDeletion = ChattyChannel.MessageDeletion(true))), ) { + @Serializable + data class Translation( + @YamlComment("The character to append on translated messages") + val translationMark: String = "𝒯", + @YamlComment("The default language for this server to translate to if a channel has unspecified targetLanguage.") + val defaultLanguage: TranslationLanguage = TranslationLanguage.English_US, + @YamlComment("The authKey for your DeepL Account. Can be found here: https://www.deepl.com/your-account/summary") + internal val authKey: String? = null, + ) + @Serializable data class Chat( val disableChatSigning: Boolean = true, diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyContext.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyContext.kt index d91705d..cc3ff4d 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyContext.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyContext.kt @@ -1,9 +1,11 @@ package com.mineinabyss.chatty +import com.deepl.api.Translator import com.mineinabyss.chatty.helpers.DiscordEmoteFixer import com.mineinabyss.chatty.queries.SpyingPlayersQuery import com.mineinabyss.geary.systems.query.CachedQuery import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.messaging.ComponentLogger const val chattyProxyChannel = "chatty:proxy" const val discordSrvChannel = "chatty:discordsrv" @@ -11,6 +13,8 @@ val chatty by DI.observe() interface ChattyContext { val plugin: ChattyPlugin val config: ChattyConfig + val logger: ComponentLogger + val translator: Translator val messages: ChattyMessages val emotefixer: DiscordEmoteFixer val isPlaceholderApiLoaded: Boolean diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyPlugin.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyPlugin.kt index 1bfecbe..4a4225a 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyPlugin.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyPlugin.kt @@ -1,9 +1,11 @@ package com.mineinabyss.chatty import com.mineinabyss.chatty.commands.ChattyBrigadierCommands +import com.deepl.api.Translator import com.mineinabyss.chatty.components.ChannelData import com.mineinabyss.chatty.components.ChattyNickname import com.mineinabyss.chatty.helpers.DiscordEmoteFixer +import com.mineinabyss.chatty.helpers.translatorStatus import com.mineinabyss.chatty.listeners.ChatListener import com.mineinabyss.chatty.listeners.ChattyProxyListener import com.mineinabyss.chatty.listeners.DiscordListener @@ -16,6 +18,8 @@ import com.mineinabyss.geary.modules.geary import com.mineinabyss.geary.systems.builders.cache import com.mineinabyss.idofront.config.config import com.mineinabyss.idofront.di.DI +import com.mineinabyss.idofront.messaging.ComponentLogger +import com.mineinabyss.idofront.messaging.observeLogger import com.mineinabyss.idofront.plugin.Plugins import com.mineinabyss.idofront.plugin.listeners import github.scarsz.discordsrv.DiscordSRV @@ -53,10 +57,13 @@ class ChattyPlugin : JavaPlugin() { } fun createChattyContext() { + translatorStatus = true DI.remove() val chattyContext = object : ChattyContext { override val plugin: ChattyPlugin = this@ChattyPlugin override val config: ChattyConfig by config("config", dataFolder.toPath(), ChattyConfig()) + override val logger: ComponentLogger by plugin.observeLogger() + override val translator: Translator = Translator(config.translation.authKey.takeUnless { it.isNullOrEmpty() } ?: "x") override val messages: ChattyMessages by config("messages", dataFolder.toPath(), ChattyMessages()) override val emotefixer: DiscordEmoteFixer by config("emotefixer", dataFolder.toPath(), DiscordEmoteFixer()) override val isPlaceholderApiLoaded: Boolean get() = Plugins.isEnabled("PlaceholderAPI") diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyTranslation.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyTranslation.kt new file mode 100644 index 0000000..cdcdb7a --- /dev/null +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyTranslation.kt @@ -0,0 +1,12 @@ +package com.mineinabyss.chatty.components + +import com.deepl.api.Language +import com.deepl.api.LanguageCode +import com.mineinabyss.chatty.chatty +import com.mineinabyss.chatty.helpers.TranslationLanguage +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("chatty:translation") +data class ChattyTranslation(val language: TranslationLanguage = chatty.config.translation.defaultLanguage, val translateSameLanguage: Boolean = false) \ No newline at end of file diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/BaseMutableMap.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/BaseMutableMap.kt new file mode 100644 index 0000000..83a5b9a --- /dev/null +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/BaseMutableMap.kt @@ -0,0 +1,121 @@ +@file:Suppress("ReplaceSizeZeroCheckWithIsEmpty") + +package com.mineinabyss.chatty.extensions + +interface BaseMutableMap : BaseMap, MutableMap { + override fun putAll(from: Map) { + for ((k, v) in from) put(k, v) + } +} + +interface BaseMap : Map { + override fun isEmpty(): Boolean = size == 0 + override fun containsKey(key: K): Boolean = keys.contains(key) + override fun containsValue(value: V): Boolean = values.contains(value) +} + +interface BaseMutableList : BaseList, MutableList { + override fun iterator(): MutableIterator = listIterator(0) + override fun listIterator(): MutableListIterator = listIterator(0) + override fun listIterator(index: Int): MutableListIterator = BaseMutableListIterator(this, index) + override fun subList(fromIndex: Int, toIndex: Int): MutableList = BaseSubMutableList(this, fromIndex, toIndex) + + override fun add(element: T): Boolean { + add(size, element) + return true + } + + override fun addAll(elements: Collection): Boolean = addAll(size, elements) + + override fun clear() { + while (isNotEmpty()) removeAt(size - 1) + } + + override fun remove(element: T): Boolean { + val index = indexOf(element) + val found = index >= 0 + if (found) removeAt(index) + return found + } + + override fun retainAll(elements: Collection): Boolean { + val set = elements.toSet() + return retainAll { it in set } + } + + override fun removeAll(elements: Collection): Boolean { + val set = elements.toSet() + return removeAll { it in set } + } +} + +interface BaseList : List { + override fun containsAll(elements: Collection): Boolean { + val elementsSet = elements.toSet() + for (n in 0 until size) if (this[n] in elementsSet) return true + return false + } + override fun contains(element: T): Boolean = indexOf(element) >= 0 + override fun isEmpty(): Boolean = size == 0 + override fun iterator(): Iterator = listIterator(0) + override fun listIterator(): ListIterator = listIterator(0) + override fun listIterator(index: Int): ListIterator = BaseListIterator(this, index) + override fun subList(fromIndex: Int, toIndex: Int): List = BaseSubList(this, fromIndex, toIndex) + override fun lastIndexOf(element: T): Int { + for (n in size - 1 downTo 0) if (this[n] == element) return n + return -1 + } + override fun indexOf(element: T): Int { + for (n in 0 until size) if (this[n] == element) return n + return -1 + } +} + +open class BaseSubList(val list: List, start: Int, end: Int) : BaseList { + var start: Int = start ; protected set + var end: Int = end ; protected set + override val size: Int get() = end - start + fun checkIndex(index: Int): Int { + if (index < 0 || index >= size) throw IndexOutOfBoundsException() + return start + index + } + + override fun get(index: Int): T = list[checkIndex(index)] +} + +open class BaseSubMutableList(val mlist: MutableList, start: Int, end: Int) : BaseSubList(mlist, start, end), + BaseMutableList { + override fun add(index: Int, element: T) { + mlist.add(checkIndex(index), element) + end++ + } + + override fun addAll(index: Int, elements: Collection): Boolean { + val before = mlist.size + val out = mlist.addAll(checkIndex(index), elements) + end += mlist.size - before + return out + } + + override fun removeAt(index: Int): T { + end-- + return mlist.removeAt(checkIndex(index)) + } + + override fun set(index: Int, element: T): T = mlist.set(checkIndex(index), element) +} + +open class BaseListIterator(val list: List, var index: Int) : ListIterator { + override fun hasNext(): Boolean = index < list.size + override fun hasPrevious(): Boolean = (index > 0) + override fun next(): T = list[index++] + override fun nextIndex(): Int = index + override fun previous(): T = list[--index] + override fun previousIndex(): Int = index - 1 +} + +open class BaseMutableListIterator(val mlist: MutableList, index: Int) : BaseListIterator(mlist, index), MutableListIterator { + override fun add(element: T) = mlist.add(index, element) + override fun remove() { mlist.removeAt(index) } + override fun set(element: T) { mlist[index] = element } +} \ No newline at end of file diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/CacheMap.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/CacheMap.kt new file mode 100644 index 0000000..9debfb1 --- /dev/null +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/extensions/CacheMap.kt @@ -0,0 +1,59 @@ +package com.mineinabyss.chatty.extensions + +open class CacheMap( + val maxSize: Int = 16, +) : BaseCacheMap() { + override fun mustFree(key: K, value: V): Boolean = size > maxSize +} + +open class BaseCacheMap() : BaseMutableMap { + val map: LinkedHashMap = LinkedHashMap() + + protected open fun mustFree(key: K, value: V): Boolean = false + protected open fun keyToRemove(key: K, value: V): K = map.keys.first() + protected open fun freed(key: K, value: V) { + } + + override val size: Int get() = map.size + + override fun remove(key: K): V? { + val value = map.remove(key) + if (value != null) freed(key, value) + return value + } + + override fun putAll(from: Map) { for ((k, v) in from) put(k, v) } + override fun put(key: K, value: V): V? { + val oldValue = map[key] + if (oldValue != value) { + remove(key) // refresh if exists + map[key] = value + putNew(key, value) + } + //while (isNotEmpty() && mustFree(key, value) && !map.containsKey(key)) { + while (isNotEmpty() && mustFree(key, value)) { + val keyToRemove = keyToRemove(key, value) + remove(keyToRemove) + } + return oldValue + } + + protected open fun putNew(key: K, value: V) { + } + + override fun clear() { + val keys = map.keys.toList() + for (key in keys) remove(key) + } + + override val entries: MutableSet> get() = map.entries + override val keys: MutableSet get() = map.keys + override val values: MutableCollection get() = map.values + + override fun get(key: K): V? = map.get(key) + + override fun toString(): String = map.toString() + + override fun equals(other: Any?): Boolean = (other is BaseCacheMap<*, *>) && (this.map == other.map) + override fun hashCode(): Int = this.map.hashCode() +} \ 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 9add1a2..1e72e00 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 @@ -11,6 +11,7 @@ object ChattyPermissions { const val BYPASS_TAG_PERM = "chatty.tags.bypass" const val BYPASS_CHAT_FILTERS_PERM = "chatty.chat.filters.bypass" const val MODERATION_PERM = "chatty.moderation" + const val BYPASS_TRANSLATION = "chatty.translation.bypass" val chatFormattingPerms = mapOf( Permission("chatty.tags.color") to StandardTags.color(), Permission("chatty.tags.rainbow") to StandardTags.rainbow(), diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/TranslationHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/TranslationHelpers.kt new file mode 100644 index 0000000..b4f9e06 --- /dev/null +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/TranslationHelpers.kt @@ -0,0 +1,106 @@ +package com.mineinabyss.chatty.helpers + +import com.mineinabyss.chatty.ChattyChannel +import com.mineinabyss.chatty.ChattyChannel.Translation.TargetLanguageType +import com.mineinabyss.chatty.chatty +import com.mineinabyss.chatty.components.ChattyTranslation +import com.mineinabyss.chatty.extensions.CacheMap +import com.mineinabyss.idofront.textcomponents.miniMsg +import com.mineinabyss.idofront.textcomponents.serialize +import net.kyori.adventure.chat.SignedMessage +import net.kyori.adventure.text.Component +import org.bukkit.entity.Player + +internal var translatorStatus: Boolean = true + +data class TranslatedMessage(val language: TranslationLanguage, val translatedMessage: Component) + +val cachedTranslations = CacheMap(8) +fun handleMessageTranslation( + source: Player, + channel: ChattyChannel, + sourceTranslation: ChattyTranslation?, + audienceTranslation: ChattyTranslation?, + component: Component, + signedMessage: SignedMessage +): Component { + if (!translatorStatus) return component + + val targetLanguage = when (channel.translation.type) { + // Force translation with targetLanguage + TargetLanguageType.FORCE -> channel.translation.targetLanguage + // If the sourceLanguage is the same as audienceLanguage, avoid translating + TargetLanguageType.SKIP_SAME_LANGUAGE -> channel.translation.targetLanguage.takeUnless { sourceTranslation != null && sourceTranslation == audienceTranslation } + // If the audience has no language set, or the source language is the same, avoid translating + TargetLanguageType.ALL_SAME_LANGUAGE -> audienceTranslation?.language?.takeUnless { it != sourceTranslation?.language } + // No translation + TargetLanguageType.NONE -> null + }?.takeUnless { source.hasPermission(ChattyPermissions.BYPASS_TRANSLATION) } ?: return component + + // Only translate if the audience has a different language set, or if it is set to translate same languages + //if (sourceTranslation?.language == targetLanguage) return component + //if (audienceTranslation?.translateSameLanguage != true && sourceTranslation?.language != null && sourceTranslation.language == audienceTranslation?.language) return component + + // We cache translations to avoid translating the same message multiple times + return cachedTranslations.entries.firstOrNull { + it.value.translatedMessage.children().dropLast(2).lastOrNull() == component.children().dropLast(2).lastOrNull() + }?.value?.translatedMessage ?: cachedTranslations.computeIfAbsent(signedMessage) { + val translatedBaseMessage = runCatching { + chatty.translator.translateText( + component.children().last().serialize(), + sourceTranslation?.language?.languageCode, + targetLanguage.languageCode + ).text.miniMsg().hoverEventShowText("Original Message: ".miniMsg().append(component.children().last())) + }.getOrElse { + if (translatorStatus) { + chatty.logger.e("DeepL API-Key Limit likely hit...") + chatty.logger.e("Stopping translations for now") + translatorStatus = false + } + return@computeIfAbsent TranslatedMessage(targetLanguage, component) + } + + TranslatedMessage( + targetLanguage, + Component.textOfChildren( + *component.children().dropLast(1).toTypedArray(), translatedBaseMessage, Component.space(), + chatty.config.translation.translationMark.miniMsg() + .hoverEventShowText("Translated Message".miniMsg()) + ) + ) + }.translatedMessage +} + +enum class TranslationLanguage(val languageCode: String) { + Bulgarian("BG"), + Czech("CS"), + Danish("DA"), + German("DE"), + Greek("EL"), + English_UK("EN-GB"), + English_US("EN-US"), + Spanish("ES"), + Estonian("ET"), + Finnish("FI"), + French("FR"), + Hungarian("HU"), + Indonesian("ID"), + Italian("IT"), + Japanese("JA"), + Korean("KO"), + Lithuanian("LT"), + Latvian("LV"), + Norwegian("NB"), + Dutch("NL"), + Polish("PL"), + Portuguese_Brazillian("PT-BR"), + Portuguese_Rest("PT-PT"), + Romanian("RO"), + Russian("RU"), + Slovak("SK"), + Slovenian("SL"), + Swedish("SV"), + Turkish("TR"), + Ukrainian("UK"), + Chinese("ZH") +} \ No newline at end of file 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 91c916c..26dfc11 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 @@ -4,6 +4,7 @@ import com.mineinabyss.chatty.ChattyChannel import com.mineinabyss.chatty.chatty import com.mineinabyss.chatty.chattyProxyChannel import com.mineinabyss.chatty.components.ChannelData +import com.mineinabyss.chatty.components.ChattyTranslation import com.mineinabyss.chatty.components.CommandSpy import com.mineinabyss.chatty.events.ChattyPlayerChatEvent import com.mineinabyss.chatty.helpers.* @@ -18,6 +19,7 @@ import io.papermc.paper.chat.ChatRenderer import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent import io.papermc.paper.event.player.AsyncChatDecorateEvent import io.papermc.paper.event.player.AsyncChatEvent +import net.kyori.adventure.chat.SignedMessage import net.kyori.adventure.text.Component import net.kyori.adventure.text.format.Style import net.kyori.adventure.text.format.TextDecoration @@ -84,33 +86,38 @@ class ChatListener : Listener { val pingedPlayer = originalMessage().serialize().checkForPlayerPings(channelId) val playerViewers = viewers().mapNotNull { it as? Player }.toSet() + val playerTranslation = player.toGearyOrNull()?.get() when { playerViewers.isEmpty() -> player.sendFormattedMessage(chatty.messages.channels.emptyChannelMessage) chatty.config.chat.disableChatSigning -> { - playerViewers.forEach { receiver -> + playerViewers.forEach { audience -> + val audienceTranslation = audience.toGearyOrNull()?.get() var finalMessage = message() - finalMessage = handleChatFilters(finalMessage, player, receiver) ?: return@forEach - finalMessage = formatPlayerPingMessage(player, pingedPlayer, receiver, finalMessage) + finalMessage = handleMessageTranslation(player, channel, playerTranslation, audienceTranslation, finalMessage, signedMessage()) + finalMessage = handleChatFilters(finalMessage, player, audience) ?: return@forEach + finalMessage = formatPlayerPingMessage(player, pingedPlayer, audience, finalMessage) finalMessage = formatModerationMessage( channel.messageDeletion, finalMessage, simpleMessage, signedMessage(), - receiver, + audience, player, playerViewers ) - receiver.sendMessage(finalMessage) + audience.sendMessage(finalMessage) } viewers().clear() - //isCancelled = true } else -> renderer { source, _, message, audience -> + if (audience !is Player) return@renderer Component.empty() + val audienceTranslation = runCatching { audience.toGearyOrNull()?.get() }.getOrNull() var finalMessage = message - finalMessage = handleChatFilters(finalMessage, player, audience as? Player) + finalMessage = handleMessageTranslation(player, channel, playerTranslation, audienceTranslation, finalMessage, signedMessage()) + finalMessage = handleChatFilters(finalMessage, player, audience) ?: return@renderer Component.empty() finalMessage = formatPlayerPingMessage(source, pingedPlayer, audience, finalMessage) finalMessage = formatModerationMessage( @@ -128,6 +135,15 @@ class ChatListener : Listener { } } + private fun handleFinalMessage(source: Player, channel: ChattyChannel, message: Component, signedMessage: SignedMessage, simpleMessage: Component, audience: Player, playerTranslation: ChattyTranslation?, audienceTranslation: ChattyTranslation?, pingedPlayer: Player?, playerViewers: Set) : Component { + return message + .let { handleMessageTranslation(source, channel, playerTranslation, audienceTranslation, it, signedMessage) } + .let { handleChatFilters(it, source, audience) } + .let { formatPlayerPingMessage(source, pingedPlayer, audience, message) } + .let { appendChannelFormat(it, source, channel) } + .let { formatModerationMessage(channel.messageDeletion, it, simpleMessage, signedMessage, audience, source, playerViewers) } + } + private fun handleProxyMessage( player: Player, channelId: String, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d54309..122723a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,3 +11,4 @@ velocity = { module = "com.velocitypowered:velocity-api", version.ref = "velocit discordsrv = { module = "com.discordsrv:discordsrv", version.ref = "discordsrv" } placeholderapi = { module = "me.clip:placeholderapi", version.ref = "placeholderapi" } imageloader = { module = "com.combimagnetron:imageloader", version.ref = "imageloader" } +deepl = { module = "com.deepl.api:deepl-java", version = "1.5.0" } \ No newline at end of file