Merge pull request #13 from NotAlexNoyle/main

Many fixes (with the help of claude)
This commit is contained in:
NotAlexNoyle authored and GitHub committed 2026-05-10 14:35:55 -04:00
commit 958a84d79a
16 files changed
+128 -116

No files matched your search

+5 -4
View File
@@ -2,10 +2,11 @@
NotAlexNoyle 05/06/26:
- Drop PlaceholderAPI dependency. Resolve Unions-OG union color tags
through MiniPlaceholders via Utilities-OG.
- Forward more types of exceptions to the server console.
- Tighten LuckPerms prefix/suffix spacing and trim handling.
- Port all uses of PlaceholderAPI to MiniPlaceholders / Utilities-OG APIs.
- Fix Unions-OG Union colors breaking MC -> Discord link.
- Drop the per-Discord-role suffix configuration. Player suffixes shown in messages now come exclusively from each player's individual LuckPerms suffix.
- Fix LuckPerms spacing so exactly one space appears between the player name and any prefix/suffix.
- Configure Color Code Roles.
NotAlexNoyle 05/04/26:
+3 -1
View File
@@ -51,13 +51,15 @@ repositories {
/* ---------------------- Java project deps ---------------------------- */
dependencies {
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") // Declare Purpur API version to be packaged.
compileOnly("net.essentialsx:EssentialsX:2.21.0") // Import EssentialsX API (internally deprecated).
compileOnly(
files("libs/AnnouncerPlus-1.4.1.jar")
) // Import AnnouncerPlus API (binary-compatible with AnnouncerPlus-OG API).
compileOnlyApi(project(":libs:Utilities-OG")) // Import TrueOG Network Utilities-OG Java API (from source).
compileOnlyApi(project(":libs:Vanish-OG")) // Import TrueOG Network Vanish-OG Java API (from source).
compileOnly("net.luckperms:api:5.5") // Import LuckPerms API.
compileOnly(
"io.github.miniplaceholders:miniplaceholders-api:2.2.3"
) // Import MiniPlaceholders API for direct tag access.
implementation("tools.jackson.core:jackson-databind:3.1.1")
implementation("tools.jackson.dataformat:jackson-dataformat-yaml:3.1.1")
@@ -1,6 +1,5 @@
package nl.skbotnl.chatog
import com.earth2me.essentials.Essentials
import java.util.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -51,7 +50,6 @@ internal class ChatOG : JavaPlugin() {
var translator: OpenAITranslator? = null
var discordBridge: DiscordBridge? = null
val discordBridgeLock = ReentrantReadWriteLock()
var essentials = Bukkit.getServer().pluginManager.getPlugin("Essentials-OG") as Essentials
var lastMessagedMap: MutableMap<UUID, UUID> = HashMap()
fun isConfigInitialized() = ::config.isInitialized
+10 -11
View File
@@ -19,8 +19,8 @@ import nl.skbotnl.chatog.ChatOG.Companion.discordBridgeLock
import nl.skbotnl.chatog.ChatOG.Companion.scope
import nl.skbotnl.chatog.util.ChatUtil
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
import nl.skbotnl.chatog.util.PlayerAffix
import nl.skbotnl.chatog.util.PlayerExtensions.chatSystem
import nl.skbotnl.chatog.util.PlayerUtils
import org.bukkit.Bukkit
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
@@ -42,7 +42,7 @@ internal class Events : Listener {
return
}
val playerPartString = ChatUtil.getPlayerPartString(event.player)
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
scope.launch {
discordBridgeLock.read {
@@ -66,7 +66,7 @@ internal class Events : Listener {
return
}
val playerPartString = ChatUtil.getPlayerPartString(event.player)
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
scope.launch {
discordBridgeLock.read {
@@ -90,7 +90,7 @@ internal class Events : Listener {
return
}
val playerPartString = ChatUtil.getPlayerPartString(event.player)
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
val reason = PlainTextComponentSerializer.plainText().serialize(event.reason())
@@ -116,7 +116,7 @@ internal class Events : Listener {
return
}
val playerPartString = ChatUtil.getPlayerPartString(event.player)
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
val advancementTitleKey = event.advancement.display?.title() ?: return
val advancementTitle = PlainTextComponentSerializer.plainText().serialize(advancementTitleKey)
@@ -188,13 +188,12 @@ internal class Events : Listener {
return
}
var nameString = "${PlayerAffix.getPrefix(event.player.uniqueId)}${event.player.name}"
var nameString = "${PlayerUtils.getPrefix(event.player.uniqueId)}${event.player.name}"
val unionColorTag =
PlainTextComponentSerializer.plainText()
.serialize(UtilitiesOG.trueogExpand("<simpleclans_union_color_tag>", event.player))
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
nameString = "&8[$unionColorTag&8] $nameString"
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", event.player)
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
nameString = "&8[$unionTag&8] $nameString"
}
val nameComponent = UtilitiesOG.trueogColorize(legacyToMm(nameString))
@@ -9,7 +9,7 @@ import net.kyori.adventure.text.event.HoverEvent
import net.trueog.utilitiesog.UtilitiesOG
import nl.skbotnl.chatog.translation.command.TranslateMessage
import nl.skbotnl.chatog.util.ChatUtil
import nl.skbotnl.chatog.util.PlayerAffix
import nl.skbotnl.chatog.util.PlayerUtils
import org.bukkit.entity.Player
internal abstract class ChatSystem {
@@ -23,13 +23,17 @@ internal abstract class ChatSystem {
var playerPartString = ChatUtil.getPlayerPartString(player)
playerPartString = listOfNotNull(prefix, playerPartString).joinToString(" | ")
sendDiscordMessage(text, playerPartString, player.uniqueId)
val discordPlayerPartString =
listOfNotNull(prefix, ChatUtil.getPlayerPartString(player, includeSuffix = true)).joinToString(" | ")
val messageComponent = ChatUtil.processText(text, player) ?: return
sendDiscordMessage(text, discordPlayerPartString, player.uniqueId)
val messageComponent =
ChatUtil.processText(text, player)?.color(PlayerUtils.getMessageColor(player.uniqueId)) ?: return
val chatComponent =
UtilitiesOG.trueogColorize(
ChatUtil.legacyToMm("$playerPartString<reset>${PlayerAffix.getSuffix(player.uniqueId)} &7> ")
ChatUtil.legacyToMm("$playerPartString<reset>${PlayerUtils.getSuffix(player.uniqueId)} &7> ")
)
var textComponent = Component.join(JoinConfiguration.noSeparators(), chatComponent, messageComponent)
@@ -23,7 +23,4 @@ constructor(
@param:JsonProperty("useColorCodeRoles") val useColorCodeRoles: Boolean,
@param:JsonProperty("colorCodeRoles") @field:JsonSetter(nulls = Nulls.AS_EMPTY) val colorCodeRoles: List<String>,
@param:JsonProperty("roles") @field:JsonSetter(nulls = Nulls.AS_EMPTY) val roles: Map<String, DiscordRoleConfig>,
@param:JsonProperty("roleSuffixes")
@field:JsonSetter(nulls = Nulls.AS_EMPTY)
val roleSuffixes: Map<String, DiscordSuffixConfig>,
)
@@ -1,6 +0,0 @@
package nl.skbotnl.chatog.config.discord
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
data class DiscordSuffixConfig @JsonCreator constructor(@param:JsonProperty("suffix") val suffix: String)
@@ -187,12 +187,7 @@ internal class DiscordBridge private constructor() {
?: NamedTextColor.GRAY
}
val suffix =
if (config.discord.roleSuffixes.isEmpty()) {
"> "
} else {
sortedRoles.firstNotNullOfOrNull { r -> config.discord.roleSuffixes[r.id] }?.suffix ?: "> "
}
val suffix = " > "
val messageComponents = mutableListOf<Component>(UtilitiesOG.trueogColorize(suffix))
@@ -368,7 +363,7 @@ internal class DiscordBridge private constructor() {
message.contentDisplay,
member.effectiveName,
Component.join(JoinConfiguration.noSeparators(), discordComponent, userComponent),
Component.text("$suffix "),
Component.text(suffix),
)
}
return discordBridge
@@ -398,7 +393,7 @@ internal class DiscordBridge private constructor() {
suspend fun sendMessage(message: String, player: Player) {
val webhookMessage =
WebhookMessageBuilder()
.setUsername(UtilitiesOG.stripFormatting(ChatUtil.getPlayerPartString(player)))
.setUsername(UtilitiesOG.stripFormatting(ChatUtil.getPlayerPartString(player, includeSuffix = true)))
.setContent(UtilitiesOG.stripFormatting(ChatUtil.stripGroupMentions(ChatUtil.convertMentions(message))))
.setAvatarUrl("https://minotar.net/helm/${player.uniqueId}.png")
@@ -4,14 +4,13 @@ import java.util.*
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.JoinConfiguration
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import net.trueog.utilitiesog.UtilitiesOG
import nl.skbotnl.chatog.ChatOG.Companion.config
import nl.skbotnl.chatog.ChatOG.Companion.languageDatabase
import nl.skbotnl.chatog.ChatOG.Companion.translator
import nl.skbotnl.chatog.util.ChatUtil
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
import nl.skbotnl.chatog.util.PlayerAffix
import nl.skbotnl.chatog.util.PlayerUtils
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
@@ -138,15 +137,15 @@ internal class TranslateMessage : CommandExecutor {
when (messageType) {
1 -> {
val sentChatMessage = sentMessage as SentChatMessage
// TODO: Should use ChatUtil.getPlayerPartString
var playerString =
"${PlayerAffix.getPrefix(sentChatMessage.player.uniqueId)}${sentChatMessage.player.name}${
PlayerAffix.getSuffix(sentChatMessage.player.uniqueId)
"${PlayerUtils.getPrefix(sentChatMessage.player.uniqueId)}${sentChatMessage.player.name}${
PlayerUtils.getSuffix(sentChatMessage.player.uniqueId)
}"
val unionColorTag =
PlainTextComponentSerializer.plainText()
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", sentMessage.player))
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
playerString = "&8[$unionColorTag&8] $playerString"
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player)
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
playerString = "&8[$unionTag&8] $playerString"
}
val playerComponent =
UtilitiesOG.trueogColorize(
@@ -155,7 +154,6 @@ internal class TranslateMessage : CommandExecutor {
)
)
// Don't convert color in translated messages
translateMessage =
Component.join(
JoinConfiguration.noSeparators(),
@@ -25,14 +25,14 @@ import org.bukkit.Sound
import org.bukkit.entity.Player
internal object ChatUtil {
fun getPlayerPartString(player: Player): String {
var playerPart = "${PlayerAffix.getPrefix(player.uniqueId)}${player.name}"
fun getPlayerPartString(player: Player, includeSuffix: Boolean = false): String {
var playerPart =
"${PlayerUtils.getPrefix(player.uniqueId)}${player.name}${if (includeSuffix) PlayerUtils.getSuffix(player.uniqueId) else ""}"
val unionColorTag =
PlainTextComponentSerializer.plainText()
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player))
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
playerPart = legacyToMm("&8[$unionColorTag&8] ") + playerPart
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player)
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
playerPart = legacyToMm("&8[$unionTag&8] ") + playerPart
}
return playerPart
@@ -102,6 +102,10 @@ internal object ChatUtil {
return legacyRegex.replace(text) { legacyToMmMap[it.groupValues[1].lowercase()] ?: it.value }
}
fun stripFormatting(text: String): String {
return MiniMessage.miniMessage().stripTags(legacyRegex.replace(text, ""))
}
private val getHandle = Regex("@([a-z0-9_.]{2,32})")
suspend fun convertMentions(text: String): String {
@@ -170,8 +174,10 @@ internal object ChatUtil {
private fun processText(text: String, player: Player?, username: String?, color: Boolean): Component? {
val words: MutableList<String> = mutableListOf()
val canUseFormatting = player?.hasPermission("chat-og.color") == true || color
val textToProcess = if (canUseFormatting) legacyToMm(text) else stripFormatting(text)
legacyToMm(text).split(" ").forEach { word ->
textToProcess.split(" ").forEach { word ->
val url = urlRegex.find(word)
if (url != null) {
@@ -206,14 +212,10 @@ internal object ChatUtil {
}
}
return if (player?.hasPermission("chat-og.color") == true) {
return if (canUseFormatting) {
colorMm.deserialize(words.joinToString(" "))
} else {
if (color) {
colorMm.deserialize(words.joinToString(" "))
} else {
noColorMm.deserialize(words.joinToString(" "))
}
noColorMm.deserialize(words.joinToString(" "))
}
}
@@ -1,23 +0,0 @@
package nl.skbotnl.chatog.util
import java.util.*
import nl.skbotnl.chatog.ChatOG.Companion.luckPerms
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
internal object PlayerAffix {
fun getPrefix(uuid: UUID): String {
val prefix = getMetaData(uuid).prefix ?: return ""
return legacyToMm(prefix).trimEnd() + " "
}
fun getSuffix(uuid: UUID): String {
val suffix = getMetaData(uuid).suffix ?: return ""
return " " + legacyToMm(suffix).trim()
}
private fun getMetaData(uuid: UUID) =
with(luckPerms.userManager.getUser(uuid) ?: luckPerms.userManager.loadUser(uuid).join()) {
val queryOptions = luckPerms.contextManager.getQueryOptions(this).orElse(queryOptions)
cachedData.getMetaData(queryOptions)
}
}
@@ -0,0 +1,24 @@
package nl.skbotnl.chatog.util
import java.util.*
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextColor
import nl.skbotnl.chatog.ChatOG.Companion.luckPerms
internal object PlayerUtils {
fun getPrefix(uuid: UUID): String {
val prefix = getMetaData(uuid).prefix?.trim() ?: return ""
return if (prefix.endsWith(' ')) prefix else "$prefix "
}
fun getSuffix(uuid: UUID) = getMetaData(uuid).suffix?.trim() ?: ""
fun getMessageColor(uuid: UUID): TextColor {
return getMetaData(uuid).getMetaValue("chat-og.messageColor")?.lowercase()?.let {
NamedTextColor.NAMES.value(it)
} ?: NamedTextColor.GRAY
}
private fun getMetaData(uuid: UUID) =
with(luckPerms.userManager.getUser(uuid) ?: luckPerms.userManager.loadUser(uuid).join()) { cachedData.metaData }
}
+48 -26
View File
@@ -64,31 +64,53 @@ discord:
listCommandText: "There are %onlineplayers% out of maximum 200 players online."
# Color code roles
# TODO: Configure these roles.
useColorCodeRoles: false
# Configure which roles can use color codes with Discord -> Minecraft
# For example:
#
# colorCodeRoles: [1234567891274567890, 0987654321983654321]
# When true, only Discord users with one of the colorCodeRoles below can use
# Utilities-OG ColorCodeMap Bukkit codes (&c, &l, &*) or MiniMessage tags
# in Discord -> Minecraft chat. All others have their input stripped of
# formatting before it is rendered in-game.
useColorCodeRoles: true
# Configure the text color (after the username (Discord -> Minecraft)) for certain roles
# For example:
#
# roles:
# 1234567891239567890:
# messageColor: red
#
# message_color can be one of the following: black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray, dark_gray, blue, green, aqua, red, light_purple, yellow, white
#
# You can also use RGB for the color:
#
# roles:
# 1234567891234567890:
# messageColor: [255, 255, 255]
# Discord role IDs allowed to use color codes from Discord.
colorCodeRoles:
- "897300356395073546" # OG-Pro
- "934876475285467247" # OG Master
- "897299946926125167" # Tester (same level as OG Master)
- "897299018407890945" # YouTube
- "1391741516204081162" # Technical Contributor
- "1096297706034569287" # Helper
- "897776965849858101" # Builder
- "916661431850639420" # MVP
- "1080912883694719047" # Developer
- "897297817574440970" # Moderator
- "897296878490763344" # Admin
# Configure the suffix for roles (has to include the space). color codes are allowed.
# For example:
# roleSuffixes:
# 1234567891234527890:
# suffix: ": "
# Default Minecraft chat color applied to a Discord -> Minecraft message when
# the sender did not type their own color code. If a user has multiple
# configured roles the entry for their highest-positioned role wins.
roles:
"897657230575431710": # Member
messageColor: gray
"897300768619659314": # OG
messageColor: white
"897300356395073546": # OG-Pro
messageColor: white
"934876475285467247": # OG Master
messageColor: white
"897299946926125167": # Tester
messageColor: white
"897299018407890945": # YouTube
messageColor: white
"1391741516204081162": # Technical Contributor
messageColor: white
"1096297706034569287": # Helper
messageColor: white
"897776965849858101": # Builder
messageColor: white
"916661431850639420": # MVP
messageColor: white
"1080912883694719047": # Developer
messageColor: white
"897297817574440970": # Moderator
messageColor: white
"897296878490763344": # Admin
messageColor: white
+1 -2
View File
@@ -7,8 +7,7 @@ depend:
- LuckPerms
- AnnouncerPlus
- Utilities-OG
softdepend:
- Essentials-OG
- Vanish-OG
commands:
translatemessage:
description: Internal command for translating messages