Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
994057e
Add Chat-Bubbles Feature
Wueffi Apr 26, 2026
50c0923
Remove unnecessary (mb) line
Wueffi Apr 26, 2026
419034b
Ready for another review round!
Wueffi Apr 27, 2026
378d034
Changes!
Wueffi Apr 27, 2026
1b3effb
Changes!
Wueffi Apr 27, 2026
828c84d
Merge remote-tracking branch 'bubbles/master'
Wueffi Apr 27, 2026
250be24
The review of the review
Wueffi May 3, 2026
7a48000
Another round of review
Wueffi May 4, 2026
62418d2
Fix handleCommandException
paulikauro May 3, 2026
06c79bd
Refactor sender and prefix construction into one method
paulikauro May 4, 2026
7cf002b
Small simplification
paulikauro May 4, 2026
0caa0a4
Remove circular dependency as best as possible
Wueffi May 25, 2026
74a76ae
Make shouting go through chat confirmations and fix two other small t…
Wueffi May 25, 2026
ea8e6d4
Reformat code!
Wueffi May 27, 2026
f56ca1a
Update Gradle (8.14.1 -> 9.5.1)
paulikauro May 31, 2026
e082e7b
Update Gradle shadow plugin (8.3.6 -> 9.4.2)
paulikauro May 31, 2026
553d3f7
Prevent test task from failing if no tests are discovered
paulikauro May 31, 2026
dc971a2
Add trailing comma
paulikauro May 31, 2026
c559499
Add chat listener registration back
paulikauro May 31, 2026
7741c16
Remove some type annotations and @Single annotations
paulikauro May 31, 2026
261ca16
Use playerOrNull, merge else if
paulikauro May 31, 2026
061dc6a
Convert getBubbles() to property instead
paulikauro May 31, 2026
becb6fd
Fix bubble list formatting
paulikauro May 31, 2026
899c06d
Add hover with bubble info on bubble messages
paulikauro May 31, 2026
db1de3c
Convert to expression body
paulikauro May 31, 2026
474ef60
Tweak bubble messages a little
paulikauro May 31, 2026
0e1571f
Add join button to bubble invites
paulikauro May 31, 2026
d40cfd2
Hide Discord chat when in bubble and showGlobalChat is off
paulikauro May 31, 2026
9c3df33
Merge branch 'master'
paulikauro Jun 3, 2026
0461665
Tweak bubble commands and update README accordingly
paulikauro Jun 3, 2026
26e9637
Add bubble command help and command descriptions
paulikauro Jun 4, 2026
1048382
Add bubble chat to commandspy
paulikauro Jun 11, 2026
9da2277
Refactor chat confirmations to fix bubble confirms going to global chat
paulikauro Jun 11, 2026
1d5bfb5
Add sendError and recolor info messages
paulikauro Jun 11, 2026
a129464
Prevent confirmations from proceeding when bubble no longer exists
paulikauro Jun 11, 2026
f71d89a
Handle global chat confirmations uniformly
paulikauro Jun 11, 2026
9600c18
Bump version
paulikauro Jun 11, 2026
2a383bf
Small cleanups
paulikauro Jun 11, 2026
32d45b7
Move shout back to Bubble.kt and echo message when global chat disabled
paulikauro Jun 11, 2026
b0a202d
Refactor spying to encapsulate spy prefix into Spying.kt
paulikauro Jun 12, 2026
bc6881f
Use bubble manager permission more in commands
paulikauro Jun 12, 2026
feb7ea6
Use UserCache when listing bubble participants
paulikauro Jun 15, 2026
4f44c9e
Add bubble members to invite, cleanup
paulikauro Jun 16, 2026
944119d
Move some command completions into better locations
paulikauro Jun 16, 2026
f30c534
Add chat confirmations to DM
paulikauro Jun 16, 2026
bcac587
Extract chat confirmations into their own file
paulikauro Jun 16, 2026
afd0950
Cache settings in memory to reduce database traffic
paulikauro Jun 16, 2026
c8d5d1d
Separate spying into command and social spy
paulikauro Jun 16, 2026
8983b98
Add /msg to social spy
paulikauro Jun 16, 2026
dbf4a7b
Compress Message.kt
paulikauro Jun 16, 2026
523f287
Make BUBBLE_OWNED consistent in casing with other constants
paulikauro Jun 16, 2026
1e41f4d
Do not show syntax when not in a bubble
paulikauro Jun 16, 2026
121ce6d
Replace Component#append with Component#textOfChildren
paulikauro Jun 16, 2026
4db62f3
Bubble create and invite take list of players to invite
paulikauro Jun 16, 2026
6583432
Refuse invites to public bubbles
paulikauro Jun 17, 2026
fd19901
Small refactor
paulikauro Jun 17, 2026
0c9a6a8
Move setting default values into Setting class
paulikauro Jun 17, 2026
756ff8b
Update Bubble command descriptions and README
paulikauro Jun 17, 2026
09f8703
Optimize imports
paulikauro Jun 17, 2026
222c5c4
Enforce more trailing commas
paulikauro Jun 17, 2026
984d0b5
Add .kotlin to gitignore
paulikauro Jun 17, 2026
722a029
Add chat confirmations to funcommands, helpop, mail and player profile
paulikauro Jun 19, 2026
b5ad8c2
Fix chat confirmations having stale Player objects and add socialspy …
paulikauro Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ Because we want to have a chat system that actually wOREks for us.
| `/profile about <player>` | `chattore.profile.about` | Set your about | `/playerprofile` |
| `/profile setabout <player> <about>` | `chattore.profile.about.others` | Set another player's about | `/playerprofile` |

## Chat-Bubble Commands

| Command | Permission | Description | Aliases |
|--------------------------------|--------------------------|-----------------------------------------------------------------|---------------------------|
| `/bubble create` | `chattore.bubble` | Create ("Blow") a Chat-Bubble | `/bb create\|/blow` |
| `/bubble invite <player>` | `chattore.bubble` | Invite Players to your (private) Chat-Bubble | `/bb invite` |
| `/bubble join <player>` | `chattore.bubble` | Join a Player's Chat-Bubble | `/bb join` |
| `/bubble leave` | `chattore.bubble` | Leave your current Chat-Bubble | `/bb leave` |
| `/bubble delete` | `chattore.bubble` | Delete ("Pop") your current Chat-Bubble | `/bb delete\|/pop` |
| `/bubble kick <player>` | `chattore.bubble` | Kick a Player from your own Chat-Bubble | `/bb kick` |
| `/bubble setPrivate <boolean>` | `chattore.bubble` | Set the visibility of your own Chat-Bubble | `/bb private` |
| `/bubble list` | `chattore.bubble` | List all Chat-Bubbles | `/bb list\|/bubbles` |
| `/shout <message>` | `chattore.bubble` | Send a message to global chat when inside a Chat-Bubble | No aliases |
| `/bubble forcedelete <player>` | `chattore.bubble.manage` | Force-Delete ("Burst") a specific Chat-Bubble | `/bb forcedelete\|/burst` |
| `seeGlobalChat <boolean>` | `chattore.bubble` | Toggles the visibility of global chat when inside a Chat-Bubble | `/gc` |

## Other Commands

| Command | Permission | Description | Aliases |
Expand Down
3 changes: 2 additions & 1 deletion chattore/src/main/kotlin/ChattORE.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ class ChattORE @Inject constructor(
}
pluginScope.apply {
val emojis = createEmojiFeature()
val messenger = createMessenger(emojis, database, luckPerms, config.format.global)
val messenger = createMessenger(emojis, database, luckPerms, config.format)
val userCache = createUserCache(database.database)
createAliasFeature()
createChatFeature(
messenger,
ChatConfirmationConfig(config.regexes),
)
createBubbleFeature(messenger, userCache, database)
createChattoreFeature()
createDiscordFeature(messenger, emojis, config.discord)
createFunCommandsFeature()
Expand Down
1 change: 1 addition & 0 deletions chattore/src/main/kotlin/ChattOREConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ data class FormatConfig(
val global: String = "<prefix> <gray>|</gray> <sender><gray>:</gray> <message>",
val join: String = "<yellow><player> has joined the network",
val leave: String = "<yellow><player> has left the network",
val bubbleChatBubble: String = "<gray>[</gray><gold>B</gold><gray>]</gray> <prefix> <gray>|</gray> <sender><gray>:</gray> <message>",
Comment thread
Wueffi marked this conversation as resolved.
Outdated
Comment thread
Wueffi marked this conversation as resolved.
Outdated
val joinDiscord: String = "**%player% has joined the network**",
val leaveDiscord: String = "**%player% has left the network**",
)
80 changes: 68 additions & 12 deletions chattore/src/main/kotlin/Messenger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@ import net.kyori.adventure.text.Component
import net.kyori.adventure.text.TextReplacementConfig
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import net.luckperms.api.LuckPerms
import org.openredstone.chattore.feature.BubbleManager
import org.openredstone.chattore.feature.DiscordBroadcastEvent
import org.openredstone.chattore.feature.Emojis
import org.openredstone.chattore.feature.NickPreset
import java.net.URI
import java.util.UUID

fun PluginScope.createMessenger(
emojis: Emojis,
database: Storage,
luckPerms: LuckPerms,
chatBroadcastFormat: String,
formatConfig: FormatConfig,
): Messenger {
val fileTypeMap = Json.parseToJsonElement(loadResourceAsString("filetypes.json"))
.jsonObject.mapValues { (_, value) -> value.jsonArray.map { it.jsonPrimitive.content } }
.onEach { (key, values) -> logger.info("Loaded ${values.size} of type $key") }
return Messenger(emojis, proxy, database, luckPerms, chatBroadcastFormat, fileTypeMap)
return Messenger(emojis, proxy, database, luckPerms, formatConfig, fileTypeMap)
}

class Messenger(
emojis: Emojis,
private val proxy: ProxyServer,
private val database: Storage,
private val luckPerms: LuckPerms,
private val chatBroadcastFormat: String,
private val formatConfig: FormatConfig,
private val fileTypeMap: Map<String, List<String>>,
) {
private val urlRegex = """<?((http|https)://([\w_-]+(?:\.[\w_-]+)+)([^\s'<>]+)?)>?""".toRegex()
Expand All @@ -45,6 +47,10 @@ class Messenger(
buildEmojiReplacement(emojis),
)

val bubbleManager = BubbleManager()
Comment thread
Wueffi marked this conversation as resolved.
Outdated
private var excludedCache: Set<UUID> = emptySet()
private var cacheDirty: Boolean = true

private fun formatReplacement(key: String, tag: String): TextReplacementConfig =
TextReplacementConfig.builder()
.match("""((\\?)(${Regex.escape(key)}(.*?)${Regex.escape(key)}))""")
Expand Down Expand Up @@ -76,16 +82,20 @@ class Messenger(
"<hover:show_text:'${player.username} | <i>Click for more</i>'><click:run_command:'/playerprofile info ${player.username}'><message></click></hover>"
.renderSimpleC(name.render(player.username))

val prefix = luckUser.cachedData.metaData.prefix
?: luckUser.primaryGroup.replaceFirstChar(Char::uppercaseChar)
val compoPrefix = getPrefixComponent(userId, player)

val compoPrefix = prefix.legacyDeserialize()
proxy.all.sendRichMessage(
chatBroadcastFormat,
"message" toC prepareChatMessage(message, player),
"sender" toC sender,
"prefix" toC compoPrefix,
)
if (cacheDirty) {
rebuildExcludedCache()
}

proxy.allPlayers.filter { it.uniqueId !in excludedCache }.forEach {
it.sendRichMessage(
formatConfig.global,
"message" toC prepareChatMessage(message, player),
"sender" toC sender,
"prefix" toC compoPrefix
)
}

val plainPrefix = PlainTextComponentSerializer.plainText().serialize(compoPrefix)
val discordBroadcast = DiscordBroadcastEvent(
Expand All @@ -97,6 +107,31 @@ class Messenger(
proxy.eventManager.fireAndForget(discordBroadcast)
}

fun broadcastBubbleMessage(player: Player, message: String) {
val userId = player.uniqueId
val userManager = luckPerms.userManager
val luckUser = userManager.getUser(userId) ?: return
val name = database.getNickname(userId) ?: NickPreset(player.username)
val sender =
Comment thread
paulikauro marked this conversation as resolved.
Outdated
"<hover:show_text:'${player.username} | <i>Click for more</i>'><click:run_command:'/playerprofile info ${player.username}'><message></click></hover>"
.renderSimpleC(name.render(player.username))

val compoPrefix = getPrefixComponent(userId, player)

val bubble = bubbleManager.getBubbleByPlayer(player)

bubble?.players?.forEach { uuid ->
val target = proxy.getPlayer(uuid).orElse(null) ?: return@forEach

target.sendRichMessage(
formatConfig.bubbleChatBubble,
"message" toC prepareChatMessage(message, player),
"sender" toC sender,
"prefix" toC compoPrefix,
)
}
}

fun prepareChatMessage(
message: String,
player: Player?,
Expand Down Expand Up @@ -145,6 +180,27 @@ class Messenger(
"</click><reset>").render()
}

fun rebuildExcludedCache() {
val bubbleExcluded = bubbleManager.getBubbles().values.flatMap { it.players }.toMutableSet()
bubbleExcluded.removeAll(database.getAllGlobalChatEnabled())

excludedCache = bubbleExcluded
cacheDirty = false
}

fun setCacheDirty() {
cacheDirty = true
}

private fun getPrefixComponent(userId: UUID, player: Player): Component {
val luckUser = luckPerms.userManager.getUser(userId) ?: return Component.empty()

val prefix = luckUser.cachedData.metaData.prefix
?: luckUser.primaryGroup.replaceFirstChar(Char::uppercaseChar)

return prefix.legacyDeserialize()
}

private fun Component.performReplacements(replacements: List<TextReplacementConfig>): Component =
replacements.fold(this, Component::replaceText)
}
7 changes: 7 additions & 0 deletions chattore/src/main/kotlin/Storage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
import org.openredstone.chattore.feature.MailboxItem
import org.openredstone.chattore.feature.NickPreset
import org.openredstone.chattore.feature.seeGlobalChatEnabled
import java.nio.file.Path
import java.util.*
import java.util.concurrent.ConcurrentHashMap
Expand Down Expand Up @@ -153,4 +154,10 @@ class Storage(
val jsonString = result[JsonSetting.value]
Json.decodeFromString<T>(jsonString)
}

fun getAllGlobalChatEnabled(): Set<UUID> = transaction(database) {
JsonSetting.selectAll().where {
(JsonSetting.key eq seeGlobalChatEnabled.key) and (JsonSetting.value eq Json.encodeToString(true))
}.map { UUID.fromString(it[JsonSetting.uuid]) }.toSet()
}
}
Loading