Port placeholders to MiniPlaceholders
Port all PlaceholderAPI usage to MiniPlaceholders / Utilities-OG APIs. Fix Unions-OG union colors breaking the 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.
This commit is contained in:
1 parent
149a16067a
commit
37237cb504
11 files changed
+166
-79
No files matched your search
+5
-4
@@ -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:
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ dependencies {
|
||||
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")
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getDiscordPlayerPartString(event.player)
|
||||
|
||||
scope.launch {
|
||||
discordBridgeLock.read {
|
||||
@@ -66,7 +66,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getDiscordPlayerPartString(event.player)
|
||||
|
||||
scope.launch {
|
||||
discordBridgeLock.read {
|
||||
@@ -90,7 +90,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getDiscordPlayerPartString(event.player)
|
||||
|
||||
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.getDiscordPlayerPartString(event.player)
|
||||
|
||||
val advancementTitleKey = event.advancement.display?.title() ?: return
|
||||
val advancementTitle = PlainTextComponentSerializer.plainText().serialize(advancementTitleKey)
|
||||
@@ -190,10 +190,9 @@ internal class Events : Listener {
|
||||
|
||||
var nameString = "${PlayerAffix.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") {
|
||||
val unionColorTag = ChatUtil.fetchUnionColorTag(event.player)
|
||||
val unionPlain = ChatUtil.stripFormatting(unionColorTag)
|
||||
if (unionPlain.isNotEmpty() && unionPlain != "None") {
|
||||
nameString = "&8[$unionColorTag&8] $nameString"
|
||||
}
|
||||
val nameComponent = UtilitiesOG.trueogColorize(legacyToMm(nameString))
|
||||
|
||||
@@ -23,7 +23,10 @@ internal abstract class ChatSystem {
|
||||
var playerPartString = ChatUtil.getPlayerPartString(player)
|
||||
playerPartString = listOfNotNull(prefix, playerPartString).joinToString(" | ")
|
||||
|
||||
sendDiscordMessage(text, playerPartString, player.uniqueId)
|
||||
val discordPlayerPartString =
|
||||
listOfNotNull(prefix, ChatUtil.getDiscordPlayerPartString(player)).joinToString(" | ")
|
||||
|
||||
sendDiscordMessage(text, discordPlayerPartString, player.uniqueId)
|
||||
|
||||
val messageComponent = ChatUtil.processText(text, player) ?: return
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package nl.skbotnl.chatog.config.discord
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.annotation.JsonSetter
|
||||
import com.fasterxml.jackson.annotation.Nulls
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class DiscordConfig
|
||||
@JsonCreator
|
||||
constructor(
|
||||
@@ -23,7 +25,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.getDiscordPlayerPartString(player)))
|
||||
.setContent(UtilitiesOG.stripFormatting(ChatUtil.stripGroupMentions(ChatUtil.convertMentions(message))))
|
||||
.setAvatarUrl("https://minotar.net/helm/${player.uniqueId}.png")
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ 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
|
||||
@@ -142,10 +141,9 @@ internal class TranslateMessage : CommandExecutor {
|
||||
"${PlayerAffix.getPrefix(sentChatMessage.player.uniqueId)}${sentChatMessage.player.name}${
|
||||
PlayerAffix.getSuffix(sentChatMessage.player.uniqueId)
|
||||
}"
|
||||
val unionColorTag =
|
||||
PlainTextComponentSerializer.plainText()
|
||||
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", sentMessage.player))
|
||||
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
|
||||
val unionColorTag = ChatUtil.fetchUnionColorTag(sentMessage.player)
|
||||
val unionPlain = ChatUtil.stripFormatting(unionColorTag)
|
||||
if (unionPlain.isNotEmpty() && unionPlain != "None") {
|
||||
playerString = "&8[$unionColorTag&8] $playerString"
|
||||
}
|
||||
val playerComponent =
|
||||
@@ -155,7 +153,6 @@ internal class TranslateMessage : CommandExecutor {
|
||||
)
|
||||
)
|
||||
|
||||
// Don't convert color in translated messages
|
||||
translateMessage =
|
||||
Component.join(
|
||||
JoinConfiguration.noSeparators(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package nl.skbotnl.chatog.util
|
||||
|
||||
import dev.minn.jda.ktx.coroutines.await
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders
|
||||
import java.util.*
|
||||
import kotlin.concurrent.read
|
||||
import net.kyori.adventure.text.Component
|
||||
@@ -9,6 +10,7 @@ import net.kyori.adventure.text.event.HoverEvent
|
||||
import net.kyori.adventure.text.format.TextColor
|
||||
import net.kyori.adventure.text.minimessage.Context
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage
|
||||
import net.kyori.adventure.text.minimessage.tag.PreProcess
|
||||
import net.kyori.adventure.text.minimessage.tag.Tag
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver
|
||||
@@ -25,19 +27,45 @@ 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 =
|
||||
"${PlayerAffix.getPrefix(player.uniqueId)}${player.name}${if (includeSuffix) PlayerAffix.getSuffix(player.uniqueId) else ""}"
|
||||
|
||||
val unionColorTag =
|
||||
PlainTextComponentSerializer.plainText()
|
||||
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player))
|
||||
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
|
||||
val unionColorTag = fetchUnionColorTag(player)
|
||||
val unionPlain = stripFormatting(unionColorTag)
|
||||
if (unionPlain.isNotEmpty() && unionPlain != "None") {
|
||||
playerPart = legacyToMm("&8[$unionColorTag&8] ") + playerPart
|
||||
}
|
||||
|
||||
return playerPart
|
||||
}
|
||||
|
||||
fun getDiscordPlayerPartString(player: Player): String {
|
||||
return getPlayerPartString(player, includeSuffix = true)
|
||||
}
|
||||
|
||||
fun fetchUnionColorTag(player: Player): String {
|
||||
var captured = ""
|
||||
val original = MiniPlaceholders.getAudiencePlaceholders(player)
|
||||
val capturingResolver =
|
||||
object : TagResolver {
|
||||
override fun resolve(name: String, arguments: ArgumentQueue, ctx: Context): Tag? {
|
||||
val tag = original.resolve(name, arguments, ctx) ?: return null
|
||||
if (tag is PreProcess) {
|
||||
captured = tag.value().replace('§', '&')
|
||||
return Tag.preProcessParsed("")
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
override fun has(name: String): Boolean = original.has(name)
|
||||
}
|
||||
val resolver =
|
||||
TagResolver.builder().resolver(MiniPlaceholders.getGlobalPlaceholders()).resolver(capturingResolver).build()
|
||||
MiniMessage.miniMessage().deserialize("<simpleclans_clan_color_tag>", resolver)
|
||||
return captured
|
||||
}
|
||||
|
||||
val mentionRegex = Regex("@([A-Za-z0-9_]{3,16})")
|
||||
|
||||
fun dingForMentions(dontDingFor: UUID, component: Component) {
|
||||
@@ -102,6 +130,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 +202,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 +240,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(" "))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,57 @@ 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() + " "
|
||||
val raw = getMetaData(uuid).prefix ?: return ""
|
||||
val cleaned = stripOuterSpaces(legacyToMm(raw))
|
||||
if (ChatUtil.stripFormatting(cleaned).isEmpty()) return ""
|
||||
return "$cleaned "
|
||||
}
|
||||
|
||||
fun getSuffix(uuid: UUID): String {
|
||||
val suffix = getMetaData(uuid).suffix ?: return ""
|
||||
return " " + legacyToMm(suffix).trim()
|
||||
val raw = getMetaData(uuid).suffix ?: return ""
|
||||
val cleaned = stripOuterSpaces(legacyToMm(raw))
|
||||
if (ChatUtil.stripFormatting(cleaned).isEmpty()) return ""
|
||||
return " $cleaned"
|
||||
}
|
||||
|
||||
private fun stripOuterSpaces(mm: String): String = stripTrailingSpaces(stripLeadingSpaces(mm))
|
||||
|
||||
private fun stripLeadingSpaces(mm: String): String {
|
||||
val keptTags = StringBuilder()
|
||||
var i = 0
|
||||
while (i < mm.length) {
|
||||
val c = mm[i]
|
||||
when {
|
||||
c.isWhitespace() -> i++
|
||||
c == '<' -> {
|
||||
val close = mm.indexOf('>', i)
|
||||
if (close < 0) break
|
||||
keptTags.append(mm, i, close + 1)
|
||||
i = close + 1
|
||||
}
|
||||
else -> return keptTags.toString() + mm.substring(i)
|
||||
}
|
||||
}
|
||||
return keptTags.toString()
|
||||
}
|
||||
|
||||
private fun stripTrailingSpaces(mm: String): String {
|
||||
val keptTags = StringBuilder()
|
||||
var end = mm.length
|
||||
while (end > 0) {
|
||||
val c = mm[end - 1]
|
||||
when {
|
||||
c.isWhitespace() -> end--
|
||||
c == '>' -> {
|
||||
val open = mm.lastIndexOf('<', end - 1)
|
||||
if (open < 0) break
|
||||
keptTags.insert(0, mm.substring(open, end))
|
||||
end = open
|
||||
}
|
||||
else -> return mm.substring(0, end) + keptTags.toString()
|
||||
}
|
||||
}
|
||||
return keptTags.toString()
|
||||
}
|
||||
|
||||
private fun getMetaData(uuid: UUID) =
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user