Many fixes (with the help of claude) #13

Merged
NotAlexNoyle merged 6 commits from main into main 2026-05-10 14:35:56 -04:00
16 changed files with 128 additions and 116 deletions

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)
2
@@ -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 {
SKBotNL commented 2026-05-07 18:30:38 -04:00 (Migrated from github.com)
Review

This looks very overengineered

This looks very overengineered
NotAlexNoyle commented 2026-05-08 00:26:19 -04:00 (Migrated from github.com)
Review

Its like that because it has to detect if union placeholder is "None" (including color codes) so union name is hidden along with its surrounding brackets. It can be simplified if those things can be hidden in a more elegant way when the player has no union.

Its like that because it has to detect if union placeholder is "None" (including color codes) so union name is hidden along with its surrounding brackets. It can be simplified if those things can be hidden in a more elegant way when the player has no union.
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