Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 |
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
73 changes: 68 additions & 5 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 All @@ -33,6 +35,11 @@ fun PluginScope.createMessenger(
return Messenger(emojis, proxy, database, luckPerms, formatConfig, fileTypeMap, wiretap, logger, userCache)
}

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

class Messenger(
emojis: Emojis,
private val proxy: ProxyServer,
Expand All @@ -55,6 +62,31 @@ class Messenger(
)
val excludedFromGlobalChat: MutableSet<UUID> = ConcurrentHashMap.newKeySet()

private val maxMessages = 100
private val messageIdCounter = AtomicInteger(0)

private fun <K, V> createMap(): MutableMap<K, V> = Collections.synchronizedMap(
object : LinkedHashMap<K, V>(maxMessages) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean =
size > maxMessages
}
)

private val messages = createMap<Int, StoredMessage>()
private val discordMessageIds = createMap<Long, Int>()

fun saveMessage(author: String, content: String, discordSnowflake: Long? = null): Int {
val messageId = messageIdCounter.incrementAndGet()
if (discordSnowflake != null) discordMessageIds[discordSnowflake] = messageId
messages[messageId] = StoredMessage(author, content)
return messageId
}

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

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

private fun formatReplacement(key: String, tag: String): TextReplacementConfig =
TextReplacementConfig.builder()
.match("""((\\?)(${Regex.escape(key)}(.*?)${Regex.escape(key)}))""")
Expand Down Expand Up @@ -90,24 +122,55 @@ 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,
) = formatConfig.chatMessage.render(
"message" toC prepareChatMessage(message, player),
"sender" toC sender,
"message" toC prepareChatMessage(message, player).withReplyClick(messageId),
"sender" toC formatSender(player),
"prefix" toC prefix,
"reply" toC formatReply(reply),
)

private fun Component.withReplyClick(messageId: Int?): Component =
if (messageId != null) {
clickEvent(ClickEvent.suggestCommand("/chatreply $messageId "))
} else {
this
}

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

fun broadcastChatMessage(player: Player, message: String) {
fun broadcastChatMessage(
player: Player,
message: String,
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))

val messageId = saveMessage(player.username, message)

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 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()
33 changes: 33 additions & 0 deletions chattore/src/main/kotlin/feature/Chat.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
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.event.Subscribe
import com.velocitypowered.api.event.player.PlayerChatEvent
import com.velocitypowered.api.proxy.Player
import org.openredstone.chattore.ChattoreException
import org.openredstone.chattore.Messenger
import org.openredstone.chattore.PluginScope
import org.openredstone.chattore.sendError
Expand All @@ -12,6 +19,7 @@ fun PluginScope.createChatFeature(
bubbleManager: BubbleManager,
) {
registerListeners(ChatListener(confirmations, messenger, bubbleManager))
registerCommands(ChatReplyCommand(confirmations, messenger))
}

private class ChatListener(
Expand Down Expand Up @@ -39,3 +47,28 @@ private class ChatListener(
}
}
}

@CommandAlias("chatreply")
@CommandPermission("chattore.chat")
private class ChatReplyCommand(
private val confirmations: ChatConfirmations,
private val messenger: Messenger,
) : BaseCommand() {
@Default
@Syntax("<id> <message>")
fun default(
sender: Player,
id: Int,
message: String,
) {
val original = messenger.getMessage(id) ?: throw ChattoreException("That message is too old!")

confirmations.submit(sender, message) { sender ->
messenger.broadcastChatMessage(
sender,
message,
reply = original,
)
}
}
}
9 changes: 8 additions & 1 deletion chattore/src/main/kotlin/feature/Discord.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ data class DiscordConfig(
val senderSpecificFormats: Map<ULong, String> = mapOf(
1234567890UL to "<red>SomeUser <gray>»<reset> <message>"
),
val ingameFormat: String = "<dark_aqua>Discord</dark_aqua> <gray>|</gray> <dark_purple><sender></dark_purple><gray>:</gray> <message>",
val ingameFormat: String = "<dark_aqua>Discord</dark_aqua> <gray>|</gray> <dark_purple><sender></dark_purple><gray><reply>:</gray> <message>",
)

// TO Discord
Expand Down Expand Up @@ -153,10 +153,17 @@ private class DiscordListener(
val url = matchResult.groupValues[2].trim()
"$text: $url"
}.replace("""\s+""".toRegex(), " ")

val referencedId = event.message.data.messageReference.value?.id?.value
val reply = referencedId?.let { messenger.getMessageByDiscordSnowflake(it.value.toLong()) }

val messageId = messenger.saveMessage(displayName, transformedMessage, event.message.id.value.toLong())

messenger.globalChat.sendRichMessage(
config.senderSpecificFormats[sender.id.value] ?: config.ingameFormat,
"sender" toS displayName,
"message" toC messenger.prepareChatMessage(transformedMessage, null),
"reply" toC messenger.formatReply(reply),
)
}
}
Expand Down
Loading