Skip to content

Commit 34b4113

Browse files
committed
feat: per-message compacts for hide groups + compact color theme
~55 beautifiers (GroupCompacts) for transitions, notifications, dungeons, abilities, combat, rewards, bazaar, slayer and events; rarity colors kept from the raw message (rare reward, mystery box). Optional theme color recolors the white words of compacts (custom RGB via §#RRGGBB in LegacyText). Fixes the dungeon key pattern that never matched.
1 parent a8db6f7 commit 34b4113

8 files changed

Lines changed: 266 additions & 8 deletions

File tree

src/main/kotlin/com/simplechat/ChatRules.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ object ChatRules {
8080
RuleAction.OFF -> Verdict.Pass
8181
RuleAction.HIDE -> Verdict.Hide
8282
RuleAction.GREY -> Verdict.Replace("§8" + clean)
83-
RuleAction.COMPACT -> Verdict.Replace(rule.beautify(clean, raw))
83+
RuleAction.COMPACT -> Verdict.Replace(theme(rule.beautify(clean, raw), cfg))
8484
RuleAction.COMPACT_GREY -> Verdict.Replace("§8" + clean(rule.beautify(clean, raw)))
8585
}
8686
}
@@ -92,7 +92,8 @@ object ChatRules {
9292
HideAction.OFF -> Verdict.Pass
9393
HideAction.HIDE -> Verdict.Hide
9494
HideAction.GREY -> Verdict.Replace("§8" + clean)
95-
HideAction.COMPACT -> Verdict.Replace(raw) // compact = garde les couleurs d'origine
95+
// Beautifier par message si dispo, sinon brut (couleurs d'origine).
96+
HideAction.COMPACT -> Verdict.Replace(GroupCompacts.beautify(clean, raw)?.let { theme(it, cfg) } ?: raw)
9697
}
9798
}
9899
// Patterns custom (toujours masqués).
@@ -238,11 +239,13 @@ object ChatRules {
238239
}),
239240
Rule("rare-reward", Category.SKYBLOCK, RuleAction.COMPACT,
240241
Pattern.compile("^RARE REWARD! .+ found a .+ in their .+ Chest!"),
241-
beautify = { c, _ ->
242+
beautify = { c, raw ->
242243
val who = Regex("RARE REWARD! (.+?) found a").find(c)?.groupValues?.get(1) ?: "?"
243-
val item = Regex("found a (.+?) in their").find(c)?.groupValues?.get(1) ?: ""
244244
val chest = Regex("in their (.+?) Chest").find(c)?.groupValues?.get(1) ?: ""
245-
"§6§lRARE REWARD §r§f$who §7· §f$item §7in §f$chest Chest"
245+
// Item pris dans le raw pour garder sa couleur de rareté.
246+
val item = GroupCompacts.rawItem(raw, "in their")
247+
?: "§f" + (Regex("found a (.+?) in their").find(c)?.groupValues?.get(1) ?: "")
248+
"§6§lRARE REWARD §r§f$who §7· §r$item §fin $chest Chest"
246249
}),
247250
Rule("gexp", Category.SKYBLOCK, RuleAction.COMPACT,
248251
Pattern.compile("^You earned [\\d,]+ GEXP \\+ [\\d,]+ Event EXP from playing"),
@@ -253,8 +256,12 @@ object ChatRules {
253256
}),
254257
)
255258

259+
/** Thème compact : recolore les mots blancs (§f) de NOS compacts avec la couleur choisie. */
260+
private fun theme(s: String, cfg: RuleConfig): String =
261+
if (!cfg.compactTheme) s else s.replace("§f", "§#%06X".format(cfg.compactThemeColor and 0xFFFFFF))
262+
256263
/** "2,637,430.3" -> "2.6M". Abrège les gros nombres pour les lignes compactes. */
257-
private fun shortNum(raw: String): String {
264+
internal fun shortNum(raw: String): String {
258265
val n = raw.replace(",", "").substringBefore(".").toLongOrNull() ?: return raw
259266
val l = java.util.Locale.ROOT
260267
return when {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.simplechat
2+
3+
import java.util.regex.Matcher
4+
import java.util.regex.Pattern
5+
6+
/**
7+
* Compacts par message pour les groupes hide-useless (action COMPACT).
8+
* Premier pattern qui matche gagne ; aucun match → null (le brut est gardé). Pur.
9+
* L'ordre compte : variantes spécifiques avant les génériques.
10+
*/
11+
object GroupCompacts {
12+
13+
private fun c(regex: String, fmt: (Matcher) -> String): Pair<Pattern, (Matcher) -> String> =
14+
Pattern.compile(regex) to fmt
15+
16+
/** Retire les articles de tête des sources ("The Mage's Magma", "Your fairy"). */
17+
private fun src(s: String) = s.removePrefix("The ").removePrefix("Your ").trim()
18+
19+
private val COMPACTS: List<Pair<Pattern, (Matcher) -> String>> = listOf(
20+
// --- Transitions (System) ---
21+
c("^Warping you to your SkyBlock island\\.\\.\\.") { "§8→ §7Island" },
22+
c("^Queuing\\.\\.\\. (.+)$") { m -> "§8⌛ §7${m.group(1)}" },
23+
c("^Welcome to Hypixel SkyBlock!$") { "§aWelcome back" },
24+
c("^Latest update: SkyBlock (.+)$") { m -> "§7Update §8· §f${m.group(1)}" },
25+
c("^You are playing on profile: (.+?)(?: \\(.+\\))?$") { m -> "§7Profile §8· §a${m.group(1)}" },
26+
c("^Watchdog has banned ([\\d,.]+) players in the last 7 days\\.$") { m -> "§cWatchdog §7· §f${m.group(1)} §7bans" },
27+
c("^You earned ([\\d,]+) Mystery Dust!$") { m -> "§7+${m.group(1)} Mystery Dust" },
28+
c("^You earned ([\\d,]+) Pet Consumables items!$") { m -> "§7+${m.group(1)} Pet Consumables" },
29+
30+
// --- Notifications (Lobby) ---
31+
c("^You tipped (\\d+) players? in (\\d+) ") { m -> "§aTipped §f${m.group(1)} §7(${m.group(2)} games)" },
32+
c("^You have just received ([\\d,]+) coins as interest") { m -> "§6+${ChatRules.shortNum(m.group(1))} coins §7(interest)" },
33+
c("^You caught yourself an invisibug!") { "§a+ §fInvisibug" },
34+
c("^Mochibear ate too much") { "§a+ §fMochibear" },
35+
c("^Achievement Unlocked: (.+)$") { m -> "§eAchievement §7· §f${m.group(1)}" },
36+
37+
// --- Dungeons ---
38+
c("^A (.+) Key was picked up!") { m -> "§a+ §f${m.group(1)} Key" },
39+
c("^(.+) opened a (?:.+ )?door!$") { m -> "§7Door §8· §f${m.group(1)}" },
40+
c("^PUZZLE SOLVED! (.+)$") { m -> "§aPuzzle ✔ §7${m.group(1)}" },
41+
c("^DUNGEON BUFF! (.+)$") { m -> "§dBuff §7· §f${m.group(1)}" },
42+
c("^A Blessing of (.+) was picked up!") { m -> "§d+ §fBlessing of ${m.group(1)}" },
43+
c("^(.+) has obtained Blessing of (.+)!") { m -> "§d+ §fBlessing of ${m.group(2)}" },
44+
c("^(.+) has obtained (Superboom TNT(?: x\\d+)?|Revive Stone|Premium Flesh|Beating Heart)!") { m -> "§a+ §f${m.group(2)} §7(${m.group(1)})" },
45+
46+
// --- Abilities ---
47+
c("on cooldown for (?:about )?(\\d+) more seconds") { m -> "§7Cooldown §8· §f${m.group(1)}s" },
48+
c("^(?:This (?:item's ability|item|ability)|Your Ultimate) is (?:temporarily disabled|on cooldown)") { "§7Cooldown" },
49+
c("mana to (?:do this|activate this)!") { "§bNot enough mana" },
50+
c("^(.+) is (?:ready to use! Press DROP|now (?:available|ready)!)") { m -> "§a✔ §f${m.group(1)} §7ready" },
51+
c("^(Archer|Mage|Berserker|Tank|Healer) Milestone (\\S+)") { m -> "§6Milestone §7· §f${m.group(1)} ${m.group(2)}" },
52+
c("^Creeper Veil (Activated|De-activated)!") { m -> if (m.group(1) == "Activated") "§7Veil on" else "§7Veil off" },
53+
54+
// --- Combat / heal ---
55+
c("^(.+?) (?:hit|burnt) you for ([\\d,.]+) (?:true )?damage") { m -> "§c-${ChatRules.shortNum(m.group(2))} §7(${src(m.group(1))})" },
56+
c("^A (.+) exploded, hitting you for ([\\d,.]+) damage") { m -> "§c-${ChatRules.shortNum(m.group(2))} §7(${m.group(1)})" },
57+
c("^You were healed for ([\\d,.]+) health") { m -> "§a+${ChatRules.shortNum(m.group(1))}" },
58+
c("^(.+?) healed you for ([\\d,.]+) health") { m -> "§a+${ChatRules.shortNum(m.group(2))}❤ §7(${src(m.group(1))})" },
59+
c("^You gained ([\\d,.]+) HP worth of absorption") { m -> "§e+${ChatRules.shortNum(m.group(1))} absorption" },
60+
c("^(.+) granted you ([\\d,.]+) strength for 20 seconds!") { m -> "§4+${m.group(2)}❁ §7(20s)" },
61+
c("^(.+) formed a tether with you!$") { m -> "§a⚯ §f${m.group(1)}" },
62+
c("^(.+) picked up your (.+) Orb!") { m -> "§d⬤ §7${m.group(2)} (${m.group(1)})" },
63+
c("^You picked up a (.+) Orb from (.+?) healing you") { m -> "§d⬤ §7${m.group(1)} (${m.group(2)})" },
64+
c("^(.+) used (.+) on you!$") { m -> "§c${m.group(2)} §7(${src(m.group(1))})" },
65+
66+
// --- Rewards / drops ---
67+
c("^ESSENCE! .+ found ([\\d,]+) (.+) Essence!") { m -> "§d+${m.group(1)} ${m.group(2)} Essence" },
68+
c(" found a Wither Essence! Everyone gains an extra essence!") { "§d+1 Wither Essence" },
69+
c("^You earned ([\\d,]+) (Event EXP|GEXP) from playing ") { m ->
70+
if (m.group(2) == "GEXP") "§2+${m.group(1)} GEXP" else "§e+${m.group(1)} Event EXP"
71+
},
72+
c("^BONUS! Temporarily earn (\\d+)% more skill experience!") { m -> "§e+${m.group(1)}% skill XP" },
73+
c("^Your Kill Combo has expired! You reached a (\\d+) Kill Combo!") { m -> "§7Combo ended §8· §f${m.group(1)}" },
74+
c("^(?:CHARM|SALT) You charmed a (.+) and captured its Shard\\.") { m -> "§a+ §fShard §7(${m.group(1)})" },
75+
76+
// --- Bazaar / AH ---
77+
c("^\\[Bazaar] Submitting sell offer") { "§6Bazaar §7· selling…" },
78+
c("^\\[Bazaar] Submitting buy order") { "§6Bazaar §7· buying…" },
79+
c("^(?:Buy Order|Sell Offer) Setup! (.+)$") { m -> "§6Bazaar §a✔ §f${m.group(1)}" },
80+
c("^Putting item in escrow") { "§7Escrow…" },
81+
c("^Setting up the auction") { "§6AH §7· setup…" },
82+
83+
// --- Slayer ---
84+
c("^SLAYER QUEST STARTED!") { "§5Slayer §7· started" },
85+
c("^SLAYER QUEST COMPLETE!") { "§5Slayer §a✔ §fcomplete" },
86+
c("^» Slay ([\\d,]+) Combat XP worth of (.+?)\\.?$") { m -> "§7Slay §f${m.group(1)} XP §7of ${m.group(2)}" },
87+
88+
// --- Events ---
89+
c("^Started parkour ") { "§bParkour §7· start" },
90+
c("^Finished parkour .+ in (.+)!$") { m -> "§bParkour §a✔ §f${m.group(1)}" },
91+
c("^Warped from the (.+) to the (.+)!$") { m -> "§8→ §7${m.group(2)}" },
92+
c("^HOPPITY'S HUNT You found an? (.+?) Egg") { m -> "§6+ §f${m.group(1)} Egg" },
93+
c("^SACRIFICE! .+ turned .+ into ([\\d,]+) Dragon Essence!") { m -> "§5Sacrifice §7· §f+${m.group(1)} Dragon Essence" },
94+
)
95+
96+
// Mystery Box : item extrait du raw pour garder sa couleur de rareté.
97+
private val BOX_IN = Pattern.compile(" found an? (.+) in an? (?:Holiday )?Mystery Box!")
98+
private val BOX_TIER = Pattern.compile(" found an? (.+) Mystery Box!$")
99+
100+
/** Segment coloré du raw entre "found a/an " et [endMarker], codes couleur conservés. */
101+
fun rawItem(raw: String, endMarker: String): String? {
102+
val m = Regex("found an? ").find(raw) ?: return null
103+
val span = raw.substring(m.range.last + 1).substringBefore(endMarker)
104+
return span.replace(Regex("(?:§.|\\s)+$"), "").trim().ifEmpty { null }
105+
}
106+
107+
/** Compact du message ou null si aucun beautifier ne matche. */
108+
fun beautify(clean: String, raw: String): String? {
109+
var m = BOX_IN.matcher(clean)
110+
if (m.find()) return "§7Mystery Box §8· §r" + (rawItem(raw, "in a") ?: "§f${m.group(1)}")
111+
m = BOX_TIER.matcher(clean)
112+
if (m.find()) return "§7Mystery Box §8· §r" + (rawItem(raw, "Mystery Box") ?: "§f${m.group(1)}")
113+
for ((pattern, fmt) in COMPACTS) {
114+
val matcher = pattern.matcher(clean)
115+
if (matcher.find()) return fmt(matcher)
116+
}
117+
return null
118+
}
119+
}

src/main/kotlin/com/simplechat/HideUseless.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object HideUseless {
4646
"HYPIXEL is hosting a .+ tournament!",
4747
),
4848
HideGroup.DUNGEONS to p(
49-
"(?:A .+ Key was picked up!\\?|RIGHT CLICK on .+ to open it\\. This key can only be used to open 1 door!|.+ opened a .+ door!|You hear the sound of something opening\\.\\.\\.|You do not have the key for this door!)",
49+
"(?:A .+ Key was picked up!?|RIGHT CLICK on .+ to open it\\. This key can only be used to open 1 door!|.+ opened a .+ door!|You hear the sound of something opening\\.\\.\\.|You do not have the key for this door!)",
5050
"(?:This lever has already been used\\.|Someone has already activated this lever!)",
5151
"(?:This chest has already been searched!|You have already opened this dungeon chest!|That chest is locked!)",
5252
"^(?:A mystical force .+|You cannot (?:use abilities|do that) in this room!)$",

src/main/kotlin/com/simplechat/LegacyText.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ object LegacyText {
2929
var i = 0
3030
while (i < input.length) {
3131
val ch = input[i]
32+
// Couleur RGB custom `§#RRGGBB` (thème compact) — hors palette vanilla.
33+
if ((ch == '§' || ch == '&') && i + 7 < input.length && input[i + 1] == '#') {
34+
val hex = input.substring(i + 2, i + 8).toIntOrNull(16)
35+
if (hex != null) {
36+
flush()
37+
color = hex; bold = false; italic = false; under = false; strike = false; obf = false
38+
i += 8
39+
continue
40+
}
41+
}
3242
if ((ch == '§' || ch == '&') && i + 1 < input.length) {
3343
val code = input[i + 1].lowercaseChar()
3444
flush()

src/main/kotlin/com/simplechat/RuleConfig.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ data class RuleConfig(
2121
val timestampColor: Int,
2222
val compactSoloClass: Boolean,
2323
val compactHoppity: Boolean,
24+
val compactTheme: Boolean = false,
25+
val compactThemeColor: Int = 0x55FFFF,
2426
) {
2527
fun actionOf(ruleId: String, default: RuleAction): RuleAction = actions[ruleId] ?: default
2628

@@ -130,6 +132,8 @@ data class RuleConfig(
130132
timestampColor = Settings.timestampColor,
131133
compactSoloClass = SkyBlockCleanup.soloClass,
132134
compactHoppity = SkyBlockCleanup.hoppity,
135+
compactTheme = Settings.compactTheme,
136+
compactThemeColor = Settings.compactThemeColor and 0xFFFFFF,
133137
)
134138
}
135139
}

src/main/kotlin/com/simplechat/Settings.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ object Settings : HscConfig("simplechat/config") {
3434
}
3535
var timestampColor by color(Color(0x555555).rgb) { name = "Timestamp color" }
3636

37+
var compactTheme by boolean(false) {
38+
name = "Compact color theme"
39+
description = "Recolor the white words of compact messages with your theme color — one consistent look"
40+
}
41+
var compactThemeColor by color(Color(0x55FFFF).rgb) { name = "Theme color" }
42+
3743
var chatTabs by boolean(true) {
3844
name = "Enable chat tabs"
3945
description = "Show the All/Party/Guild tab row above the chat input"

src/main/kotlin/com/simplechat/menu/MenuScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ class MenuScreen(private val parent: Screen?) : Screen(
750750
// Sections d'une catégorie SANS onglets (header par section ; titre vide = pas de header).
751751
private val FLAT_SECTIONS: Map<String?, LinkedHashMap<String, List<String>>> = mapOf(
752752
null to linkedMapOf(
753-
"" to listOf("masterEnabled", "groupingWindowSeconds", "smartCollapse", "updateNotifications", "maxMessages", "showTimestamps", "timestampColor"),
753+
"" to listOf("masterEnabled", "groupingWindowSeconds", "smartCollapse", "updateNotifications", "maxMessages", "showTimestamps", "timestampColor", "compactTheme", "compactThemeColor"),
754754
"CHAT TABS" to listOf("chatTabs", "tabFilterMode"),
755755
),
756756
)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.simplechat
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Test
5+
6+
/** Compacts par message des groupes hide-useless (action COMPACT) + thème couleur. */
7+
class GroupCompactsTest {
8+
9+
private fun cfg(group: HideGroup) =
10+
RuleConfig.DEFAULT.copy(hideGroupActions = mapOf(group to HideAction.COMPACT))
11+
12+
@Test fun `transitions compacted`() {
13+
val c = cfg(HideGroup.TRANSITIONS)
14+
assertEquals(Verdict.Replace("§8⌛ §7Bedwars 4v4"),
15+
ChatRules.evaluate("Queuing... Bedwars 4v4", c))
16+
assertEquals(Verdict.Replace("§aWelcome back"),
17+
ChatRules.evaluate("§eWelcome to Hypixel SkyBlock!", c))
18+
assertEquals(Verdict.Replace("§7Profile §8· §aMango"),
19+
ChatRules.evaluate("You are playing on profile: Mango (Co-op)", c))
20+
}
21+
22+
@Test fun `notifications compacted`() {
23+
val c = cfg(HideGroup.NOTIFICATIONS)
24+
assertEquals(Verdict.Replace("§aTipped §f12 §7(4 games)"),
25+
ChatRules.evaluate("§aYou tipped 12 players in 4 games!", c))
26+
assertEquals(Verdict.Replace("§eAchievement §7· §fBedwars Banker"),
27+
ChatRules.evaluate("§eAchievement Unlocked: Bedwars Banker", c))
28+
}
29+
30+
@Test fun `dungeons compacted`() {
31+
val c = cfg(HideGroup.DUNGEONS)
32+
assertEquals(Verdict.Replace("§a+ §fWither Key"),
33+
ChatRules.evaluate("§8A §5Wither Key §8was picked up!", c))
34+
assertEquals(Verdict.Replace("§d+ §fBlessing of Power V"),
35+
ChatRules.evaluate("§dA Blessing of Power V was picked up!", c))
36+
}
37+
38+
@Test fun `abilities compacted`() {
39+
val c = cfg(HideGroup.ABILITIES)
40+
assertEquals(Verdict.Replace("§7Cooldown §8· §f2s"),
41+
ChatRules.evaluate("§cThis ability is on cooldown for 2 more seconds.", c))
42+
assertEquals(Verdict.Replace("§bNot enough mana"),
43+
ChatRules.evaluate("§cYou do not have enough mana to do this!", c))
44+
}
45+
46+
@Test fun `combat and heal compacted`() {
47+
val c = cfg(HideGroup.COMBAT)
48+
assertEquals(Verdict.Replace("§c-1.2k §7(Goldor's TNT Trap)"),
49+
ChatRules.evaluate("§cGoldor's TNT Trap hit you for 1,200 true damage", c))
50+
assertEquals(Verdict.Replace("§a+500❤ §7(MeteoFrance)"),
51+
ChatRules.evaluate("§aMeteoFrance healed you for 500 health!", c))
52+
}
53+
54+
@Test fun `rewards compacted`() {
55+
val c = cfg(HideGroup.REWARDS)
56+
assertEquals(Verdict.Replace("§e+633 Event EXP"),
57+
ChatRules.evaluate("§eYou earned 633 Event EXP from playing SkyBlock!", c))
58+
assertEquals(Verdict.Replace("§7Combo ended §8· §f30"),
59+
ChatRules.evaluate("§cYour Kill Combo has expired! You reached a 30 Kill Combo!", c))
60+
}
61+
62+
@Test fun `bazaar compacted`() {
63+
val c = cfg(HideGroup.BAZAAR)
64+
assertEquals(Verdict.Replace("§6Bazaar §7· selling…"),
65+
ChatRules.evaluate("§6[Bazaar] §7Submitting sell offer...", c))
66+
}
67+
68+
@Test fun `slayer compacted`() {
69+
val c = cfg(HideGroup.SLAYER)
70+
assertEquals(Verdict.Replace("§5Slayer §7· started"),
71+
ChatRules.evaluate(" §5§lSLAYER QUEST STARTED!", c))
72+
}
73+
74+
@Test fun `events compacted`() {
75+
val c = cfg(HideGroup.EVENTS)
76+
assertEquals(Verdict.Replace("§bParkour §a✔ §f1:23"),
77+
ChatRules.evaluate("§bFinished parkour Foraging Island in 1:23!", c))
78+
assertEquals(Verdict.Replace("§8→ §7Castle"),
79+
ChatRules.evaluate("§aWarped from the Spawn to the Castle!", c))
80+
}
81+
82+
@Test fun `unknown group message keeps raw on compact`() {
83+
val c = cfg(HideGroup.MISC)
84+
assertEquals(Verdict.Replace("§cInventory full? Don't forget to check out your Storage inside the SkyBlock Menu!"),
85+
ChatRules.evaluate("§cInventory full? Don't forget to check out your Storage inside the SkyBlock Menu!", c))
86+
}
87+
88+
@Test fun `rare reward keeps item rarity color`() {
89+
assertEquals(Verdict.Replace("§6§lRARE REWARD §r§fTioLDK §7· §r§9Fuming Potato Book §fin Bedrock Chest"),
90+
ChatRules.evaluate("§6§lRARE REWARD! §fTioLDK §efound a §9Fuming Potato Book §ein their §aBedrock Chest§e!", RuleConfig.DEFAULT))
91+
}
92+
93+
@Test fun `mystery box keeps item rarity color`() {
94+
val c = cfg(HideGroup.TRANSITIONS)
95+
assertEquals(Verdict.Replace("§7Mystery Box §8· §r§5Epic Rune"),
96+
ChatRules.evaluate("§bMeteo §efound a §5Epic Rune §ein a §6Mystery Box!", c))
97+
}
98+
99+
@Test fun `theme recolors white words of compacts`() {
100+
val themed = RuleConfig.DEFAULT.copy(
101+
actions = mapOf("pet-spawn" to RuleAction.COMPACT),
102+
compactTheme = true, compactThemeColor = 0xFF00FF,
103+
)
104+
assertEquals(Verdict.Replace("§a+ §#FF00FFBat"),
105+
ChatRules.evaluate("Spawned your Bat!", themed))
106+
}
107+
108+
@Test fun `legacy parser reads custom hex color`() {
109+
assertEquals(listOf(Seg("+ ", 0x55FF55), Seg("Bat", 0xFF00FF)),
110+
LegacyText.parse("§a+ §#FF00FFBat"))
111+
}
112+
}

0 commit comments

Comments
 (0)