Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ Because we want to have a chat system that actually wOREks for us.

## Other Commands

| Command | Permission | Description | Aliases |
|-------------------------|-----------------------|-------------------------------|------------|
| `/chattore reload` | `chattore.manage` | Reload Chattore configuration | No aliases |
| `/commandspy` | `chattore.commandspy` | Toggle spying on commands | No aliases |
| `/chattore version` | `chattore.manage` | View the version of Chattore | No aliases |
| `/emoji <emoji_names>+` | `chattore.emoji` | View multiple emojis | No aliases |
| Command | Permission | Description | Aliases |
|---------------------------------|-----------------------|-------------------------------|------------|
| `/chattore reload` | `chattore.manage` | Reload Chattore configuration | No aliases |
| `/commandspy` | `chattore.commandspy` | Toggle spying on commands | No aliases |
| `/chattore version` | `chattore.manage` | View the version of Chattore | No aliases |
| `/emoji <emoji_names>+` | `chattore.emoji` | View multiple emojis | No aliases |
| `/chatreply <id> <message>` | `chattore.chat` | Reply to a chat message | No aliases |
7 changes: 4 additions & 3 deletions chattore/src/main/kotlin/ChattORE.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ class ChattORE @Inject constructor(
val wiretap = createSpyingFeature(database, config.format)
val messenger = createMessenger(emojis, database, luckPerms, config.format, wiretap, userCache)
val chatConfirmations = createChatConfirmations(ChatConfirmationConfig(config.regexes))
val bubbleManager = createBubbleFeature(messenger, database, chatConfirmations, config.format, userCache)
val chatReply = createChatReplyFeature(messenger, chatConfirmations)
val bubbleManager = createBubbleFeature(messenger, database, chatConfirmations, config.format, userCache, chatReply)
createAliasFeature()
createChatFeature(messenger, chatConfirmations, bubbleManager)
createChatFeature(messenger, chatConfirmations, bubbleManager, chatReply)
createChattoreFeature()
createDiscordFeature(messenger, emojis, config.discord)
createDiscordFeature(messenger, emojis, config.discord, chatReply)
createFunCommandsFeature(chatConfirmations)
createHelpOpFeature(chatConfirmations)
createJoinLeaveFeature(config.format)
Expand Down
2 changes: 1 addition & 1 deletion chattore/src/main/kotlin/ChattOREConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ data class ChattOREConfig(
)

data class FormatConfig(
val chatMessage: String = "<prefix> <gray>|</gray> <sender><gray>:</gray> <message>",
val chatMessage: String = "<prefix> <gray>|</gray> <sender><reply><gray>:</gray> <message>",
val join: String = "<yellow><player> has joined the network",
val leave: String = "<yellow><player> has left the network",
val bubblePrefix: String = "<bubble_info>\uD83D\uDCAC</bubble_info>",
Expand Down
41 changes: 37 additions & 4 deletions chattore/src/main/kotlin/Messenger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import kotlinx.serialization.json.jsonPrimitive
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.Component.space
import net.kyori.adventure.text.TextReplacementConfig
import net.kyori.adventure.text.event.ClickEvent
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import net.luckperms.api.LuckPerms
Expand All @@ -17,6 +18,7 @@ import org.slf4j.Logger
import java.net.URI
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
Comment thread
Wueffi marked this conversation as resolved.
import kotlin.jvm.optionals.getOrNull

fun PluginScope.createMessenger(
Expand Down Expand Up @@ -90,24 +92,49 @@ class Messenger(
.renderSimpleC(name.render(player.username))
}

fun formatReply(reply: StoredMessage?): Component {
if (reply == null) return Component.empty()
val originalMessage = reply.content.replace("'", "\\'")
return " <hover:show_text:'<aqua>${reply.author}</aqua><gray>:</gray> $originalMessage'><gray>↪ ${reply.author}</gray></hover>"
.render()
}

fun formatChatMessage(
message: String,
player: Player,
sender: Component = formatSender(player),
prefix: Component = formatPrefix(player),
messageId: Int? = null,
reply: StoredMessage? = null,
replyComponent: Component = formatReply(reply)
Comment thread
Wueffi marked this conversation as resolved.
Outdated
) = formatConfig.chatMessage.render(
"message" toC prepareChatMessage(message, player),
"message" toC prepareChatMessage(message, player, messageId),
"sender" toC sender,
"prefix" toC prefix,
"reply" toC replyComponent,
)

val globalChat = proxy.all { it.uniqueId !in excludedFromGlobalChat }

fun broadcastChatMessage(player: Player, message: String) {
fun broadcastChatMessage(
player: Player,
message: String,
messageId: Int,
reply: StoredMessage? = null,
) {
logger.info("${player.username} (${player.uniqueId}): $message")
val originServer = player.currentServer.getOrNull()?.serverInfo?.name ?: "VOID"
val compoPrefix = formatPrefix(player)
globalChat.sendMessage(formatChatMessage(message, player, prefix = compoPrefix))

globalChat.sendMessage(
formatChatMessage(
message,
player,
prefix = compoPrefix,
messageId = messageId,
reply = reply,
)
Comment thread
Wueffi marked this conversation as resolved.
)

val plainPrefix = PlainTextComponentSerializer.plainText().serialize(compoPrefix)
val discordBroadcast = DiscordBroadcastEvent(
Expand All @@ -134,6 +161,7 @@ class Messenger(
fun prepareChatMessage(
message: String,
player: Player?,
messageId: Int? = null,
): Component {
val canObfuscate = player?.hasPermission("chattore.chat.obfuscate") ?: false
val parts = urlRegex.split(message)
Expand All @@ -146,7 +174,12 @@ class Messenger(
builder.append(formatLink(nextMatch.groupValues[1]))
}
}
return builder.build().performReplacements(chatReplacements)
val content = builder.build().performReplacements(chatReplacements)
return if (messageId != null) {
content.clickEvent(ClickEvent.suggestCommand("/chatreply $messageId "))
Comment thread
Wueffi marked this conversation as resolved.
Outdated
} else {
content
}
}

private fun formatLink(str: String): Component {
Expand Down
36 changes: 18 additions & 18 deletions chattore/src/main/kotlin/Pride.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package org.openredstone.chattore

fun weighted(vararg colors: Pair<String, Int>): Array<String> =
colors.flatMap { (value, weight) -> List(weight) { value } }.toTypedArray()
colors.flatMap { (value, weight) -> List(weight) { value } }.toTypedArray()

val prideColors = mapOf(
"rainbow" to arrayOf(
Expand Down Expand Up @@ -331,14 +331,14 @@ val prideColors = mapOf(
),

"boyflux2" to weighted(
Pair("#E48AE4",1),
Pair("#9A81B4",1),
Pair("#55BFAB",1),
Pair("#FFFFFF",1),
Pair("#A8A8A8",1),
Pair("#81D5EF",5),
Pair("#69ABE5",5),
Pair("#5276D4",5),
Pair("#E48AE4", 1),
Pair("#9A81B4", 1),
Pair("#55BFAB", 1),
Pair("#FFFFFF", 1),
Pair("#A8A8A8", 1),
Pair("#81D5EF", 5),
Pair("#69ABE5", 5),
Pair("#5276D4", 5),
),

"girlflux" to arrayOf(
Expand Down Expand Up @@ -395,13 +395,13 @@ val prideColors = mapOf(
),

"gendernonconforming1" to weighted(
Pair("#50284D",4),
Pair("#96467B",1),
Pair("#5C96F7",1),
Pair("#FFE6F7",1),
Pair("#5C96F7",1),
Pair("#96467B",1),
Pair("#50284D",4),
Pair("#50284D", 4),
Pair("#96467B", 1),
Pair("#5C96F7", 1),
Pair("#FFE6F7", 1),
Pair("#5C96F7", 1),
Pair("#96467B", 1),
Pair("#50284D", 4),
),

"gendernonconforming2" to arrayOf(
Expand Down Expand Up @@ -520,5 +520,5 @@ val prideColors = mapOf(
)

val pridePresets = prideColors.mapValues { (_, colors) ->
"<gradient:${colors.joinToString(':'.toString())}><username></gradient>"
}.toSortedMap()
"<gradient:${colors.joinToString(':'.toString())}><username></gradient>"
}.toSortedMap()
6 changes: 5 additions & 1 deletion chattore/src/main/kotlin/feature/Bubble.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ fun PluginScope.createBubbleFeature(
chatConfirmations: ChatConfirmations,
formatConfig: FormatConfig,
userCache: UserCache,
chatReply: ChatReply,
): BubbleManager {
val bubbleManager = BubbleManager()
commandManager.apply {
Expand Down Expand Up @@ -66,6 +67,7 @@ fun PluginScope.createBubbleFeature(
chatConfirmations,
formatConfig,
userCache,
chatReply,
),
)
return bubbleManager
Expand All @@ -81,6 +83,7 @@ private class BubbleCommand(
private val chatConfirmations: ChatConfirmations,
private val formatConfig: FormatConfig,
private val userCache: UserCache,
private val chatReply: ChatReply,
) : BaseCommand() {

@CatchUnknown
Expand Down Expand Up @@ -289,7 +292,8 @@ private class BubbleCommand(
@Description("Send a message to global chat when in a bubble")
fun shout(sender: Player, message: String) {
chatConfirmations.submit(sender, message) { sender ->
messenger.broadcastChatMessage(sender, message)
val messageId = chatReply.saveMessage(sender.username, message)
messenger.broadcastChatMessage(sender, message, messageId)
if (sender.uniqueId in messenger.excludedFromGlobalChat) {
sender.sendMessage(
textOfChildren(
Expand Down
7 changes: 5 additions & 2 deletions chattore/src/main/kotlin/feature/Chat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ fun PluginScope.createChatFeature(
messenger: Messenger,
confirmations: ChatConfirmations,
bubbleManager: BubbleManager,
chatReply: ChatReply,
) {
registerListeners(ChatListener(confirmations, messenger, bubbleManager))
registerListeners(ChatListener(confirmations, messenger, bubbleManager, chatReply))
}

private class ChatListener(
private val confirmations: ChatConfirmations,
private val messenger: Messenger,
private val bubbleManager: BubbleManager,
private val chatReply: ChatReply,
) {
@Subscribe
fun onChatEvent(event: PlayerChatEvent) {
Expand All @@ -26,7 +28,8 @@ private class ChatListener(
val bubble = bubbleManager.getBubbleByPlayer(player)
if (bubble == null) {
confirmations.submit(player, message) { player ->
messenger.broadcastChatMessage(player, message)
val messageId = chatReply.saveMessage(player.username, message)
messenger.broadcastChatMessage(player, message, messageId)
}
return
}
Expand Down
99 changes: 99 additions & 0 deletions chattore/src/main/kotlin/feature/ChatReply.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.openredstone.chattore.feature

import co.aikar.commands.BaseCommand
import co.aikar.commands.annotation.CommandAlias
import co.aikar.commands.annotation.CommandPermission
import co.aikar.commands.annotation.Default
import co.aikar.commands.annotation.Syntax
import com.velocitypowered.api.proxy.Player
import org.openredstone.chattore.ChattoreException
import org.openredstone.chattore.Messenger
import org.openredstone.chattore.PluginScope
import java.util.Collections
import java.util.LinkedHashMap
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger

data class StoredMessage(
val author: String,
val content: String,
)

class ChatReply(
private val confirmations: ChatConfirmations,
) {
private val maxMessages = 100
private val messageIdCounter = AtomicInteger(0)

private val messages: MutableMap<Int, StoredMessage> = Collections.synchronizedMap(
Comment thread
Wueffi marked this conversation as resolved.
Outdated
object : LinkedHashMap<Int, StoredMessage>(maxMessages) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<Int, StoredMessage>?): Boolean =
size > maxMessages
}
)

private val discordMessageIds: MutableMap<Long, Int> = ConcurrentHashMap()
Comment thread
Wueffi marked this conversation as resolved.
Outdated

fun saveMessage(author: String, content: String): Int {
val messageId = messageIdCounter.incrementAndGet()
messages[messageId] = StoredMessage(author, content)
return messageId
}

fun getMessage(id: Int): StoredMessage? = messages[id]

fun linkDiscordMessage(discordSnowflake: Long, messageId: Int) {
Comment thread
Wueffi marked this conversation as resolved.
Outdated
discordMessageIds[discordSnowflake] = messageId
}

fun getMessageByDiscordSnowflake(discordSnowflake: Long): StoredMessage? =
discordMessageIds[discordSnowflake]?.let { getMessage(it) }

fun reply(
Comment thread
Wueffi marked this conversation as resolved.
Outdated
sender: Player,
messenger: Messenger,
id: Int,
message: String,
) {
val original = getMessage(id) ?: throw ChattoreException("That message is too old!")

confirmations.submit(sender, message) {
val newMessageId = saveMessage(sender.username, message)
messenger.broadcastChatMessage(
sender,
message,
newMessageId,
reply = original,
)
}
}
}

fun PluginScope.createChatReplyFeature(
messenger: Messenger,
confirmations: ChatConfirmations,
): ChatReply {
val chatReply = ChatReply(confirmations)

@CommandAlias("chatreply")
@CommandPermission("chattore.chat")
class ChatReplyCommand : BaseCommand() {
@Default
@Syntax("<id> <message>")
fun default(
sender: Player,
id: Int,
message: String,
) {
chatReply.reply(
sender = sender,
messenger = messenger,
id = id,
message = message,
)
}
}

registerCommands(ChatReplyCommand())
return chatReply
}
Comment thread
Wueffi marked this conversation as resolved.
Outdated
Loading