diff --git a/gradle.properties b/gradle.properties index 5985a33f..29a7d70e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Project info -version=1.5.0-alpha-8 +version=1.5.0-rc-1 group=com.rose.gateway # Plugin configuration # https://github.com/pinterest/ktlint diff --git a/src/main/kotlin/com/rose/gateway/config/access/AboutExtensionEnabled.kt b/src/main/kotlin/com/rose/gateway/config/access/AboutExtensionEnabled.kt new file mode 100644 index 00000000..dc744a39 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/AboutExtensionEnabled.kt @@ -0,0 +1,12 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the about extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.aboutExtensionEnabled(): Boolean { + return config.bot.extensions.about.enabled +} diff --git a/src/main/kotlin/com/rose/gateway/config/access/BotConfiguration.kt b/src/main/kotlin/com/rose/gateway/config/access/BotConfiguration.kt new file mode 100644 index 00000000..1660c8b7 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/BotConfiguration.kt @@ -0,0 +1,21 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives the Discord bot token from the plugin config + * + * @return The Discord bot's token + */ +fun PluginConfig.botToken(): String { + return config.bot.token +} + +/** + * Gives the bot channels from the plugin config + * + * @return The Discord bot's bot channels + */ +fun PluginConfig.botChannels(): List { + return config.bot.botChannels +} diff --git a/src/main/kotlin/com/rose/gateway/config/access/ChatExtensionEnabled.kt b/src/main/kotlin/com/rose/gateway/config/access/ChatExtensionEnabled.kt new file mode 100644 index 00000000..53d8167c --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/ChatExtensionEnabled.kt @@ -0,0 +1,12 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the chat extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.chatExtensionEnabled(): Boolean { + return config.bot.extensions.chat.enabled +} diff --git a/src/main/kotlin/com/rose/gateway/config/access/IpExtension.kt b/src/main/kotlin/com/rose/gateway/config/access/IpExtension.kt new file mode 100644 index 00000000..836d90d0 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/IpExtension.kt @@ -0,0 +1,21 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the ip extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.ipExtensionEnabled(): Boolean { + return config.bot.extensions.ip.enabled +} + +/** + * The IP to display with the IP extension + * + * @return The IP to display + */ +fun PluginConfig.displayIp(): String { + return config.bot.extensions.ip.displayIp +} diff --git a/src/main/kotlin/com/rose/gateway/config/access/ListExtension.kt b/src/main/kotlin/com/rose/gateway/config/access/ListExtension.kt new file mode 100644 index 00000000..a9dcad40 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/ListExtension.kt @@ -0,0 +1,21 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the list extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.listExtensionEnabled(): Boolean { + return config.bot.extensions.list.enabled +} + +/** + * Gives the maximum number of players that can be displayed on each list page + * + * @return The maximum number of players per page + */ +fun PluginConfig.maxPlayersPerListPage(): Int { + return config.bot.extensions.list.maxPlayersPerPage +} diff --git a/src/main/kotlin/com/rose/gateway/config/extensions/MinecraftConfig.kt b/src/main/kotlin/com/rose/gateway/config/access/MinecraftConfig.kt similarity index 96% rename from src/main/kotlin/com/rose/gateway/config/extensions/MinecraftConfig.kt rename to src/main/kotlin/com/rose/gateway/config/access/MinecraftConfig.kt index 98de4ec2..87ad10b4 100644 --- a/src/main/kotlin/com/rose/gateway/config/extensions/MinecraftConfig.kt +++ b/src/main/kotlin/com/rose/gateway/config/access/MinecraftConfig.kt @@ -1,4 +1,4 @@ -package com.rose.gateway.config.extensions +package com.rose.gateway.config.access import com.rose.gateway.config.PluginConfig import com.rose.gateway.minecraft.color.asTextColor diff --git a/src/main/kotlin/com/rose/gateway/config/access/TpsExtensionEnabled.kt b/src/main/kotlin/com/rose/gateway/config/access/TpsExtensionEnabled.kt new file mode 100644 index 00000000..c2119d5a --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/TpsExtensionEnabled.kt @@ -0,0 +1,12 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the TPS extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.tpsExtensionEnabled(): Boolean { + return config.bot.extensions.tps.enabled +} diff --git a/src/main/kotlin/com/rose/gateway/config/access/WhitelistExtension.kt b/src/main/kotlin/com/rose/gateway/config/access/WhitelistExtension.kt new file mode 100644 index 00000000..76f15606 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/config/access/WhitelistExtension.kt @@ -0,0 +1,21 @@ +package com.rose.gateway.config.access + +import com.rose.gateway.config.PluginConfig + +/** + * Gives whether the whitelist extension is enabled from the plugin config + * + * @return Whether the extension is enabled + */ +fun PluginConfig.whitelistExtensionEnabled(): Boolean { + return config.bot.extensions.whitelist.enabled +} + +/** + * Gives the maximum number of players that can be displayed on each whitelist page + * + * @return The maximum number of players per page + */ +fun PluginConfig.maxPlayersPerWhitelistPage(): Int { + return config.bot.extensions.whitelist.maxPlayersPerPage +} diff --git a/src/main/kotlin/com/rose/gateway/config/extensions/BotConfiguration.kt b/src/main/kotlin/com/rose/gateway/config/extensions/BotConfiguration.kt deleted file mode 100644 index ac860cd0..00000000 --- a/src/main/kotlin/com/rose/gateway/config/extensions/BotConfiguration.kt +++ /dev/null @@ -1,84 +0,0 @@ -package com.rose.gateway.config.extensions - -import com.rose.gateway.config.PluginConfig - -/** - * Gives the Discord bot token from the plugin config - * - * @return The Discord bot's token - */ -fun PluginConfig.botToken(): String { - return config.bot.token -} - -/** - * Gives the bot channels from the plugin config - * - * @return The Discord bot's bot channels - */ -fun PluginConfig.botChannels(): List { - return config.bot.botChannels -} - -/** - * Gives whether the about extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.aboutExtensionEnabled(): Boolean { - return config.bot.extensions.about.enabled -} - -/** - * Gives whether the chat extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.chatExtensionEnabled(): Boolean { - return config.bot.extensions.chat.enabled -} - -/** - * Gives whether the ip extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.ipExtensionEnabled(): Boolean { - return config.bot.extensions.ip.enabled -} - -/** - * The IP to display with the IP extension - * - * @return The IP to display - */ -fun PluginConfig.displayIp(): String { - return config.bot.extensions.ip.displayIp -} - -/** - * Gives whether the list extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.listExtensionEnabled(): Boolean { - return config.bot.extensions.list.enabled -} - -/** - * Gives whether the TPS extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.tpsExtensionEnabled(): Boolean { - return config.bot.extensions.tps.enabled -} - -/** - * Gives whether the whitelist extension is enabled from the plugin config - * - * @return Whether the extension is enabled - */ -fun PluginConfig.whitelistExtensionEnabled(): Boolean { - return config.bot.extensions.whitelist.enabled -} diff --git a/src/main/kotlin/com/rose/gateway/config/schema/ListConfig.kt b/src/main/kotlin/com/rose/gateway/config/schema/ListConfig.kt index fadaa227..0bc92e3b 100644 --- a/src/main/kotlin/com/rose/gateway/config/schema/ListConfig.kt +++ b/src/main/kotlin/com/rose/gateway/config/schema/ListConfig.kt @@ -1,6 +1,7 @@ package com.rose.gateway.config.schema import com.rose.gateway.config.markers.CommonExtensionConfig +import com.rose.gateway.config.markers.ConfigItem import com.rose.gateway.discord.bot.extensions.list.ListExtension import com.rose.gateway.shared.serialization.SurrogateBasedSerializer import com.rose.gateway.shared.serialization.SurrogateConverter @@ -15,24 +16,31 @@ import kotlinx.serialization.Serializable */ @Serializable(with = ListConfigSerializer::class) class ListConfig( - enabled: Boolean + enabled: Boolean, + /** + * The maximum number of player allowed per list page + */ + @ConfigItem("The maximum number of player allowed per list page") var maxPlayersPerPage: Int ) : CommonExtensionConfig(enabled, ListExtension.extensionName()) /** * Surrogate for serialization of [ListConfig] * * @property enabled Whether the extension is enabled + * @property maxPlayersPerPage The maximum number of player allowed per list page * @constructor Create a "list config" surrogate with the provided data * * @see ListConfig * @see ListConfigSerializer */ @Serializable -data class ListConfigSurrogate(val enabled: Boolean) { +data class ListConfigSurrogate(val enabled: Boolean, val maxPlayersPerPage: Int) { companion object : SurrogateConverter { - override fun fromBase(base: ListConfig): ListConfigSurrogate = ListConfigSurrogate(base.enabled) + override fun fromBase(base: ListConfig): ListConfigSurrogate = + ListConfigSurrogate(base.enabled, base.maxPlayersPerPage) - override fun toBase(surrogate: ListConfigSurrogate): ListConfig = ListConfig(surrogate.enabled) + override fun toBase(surrogate: ListConfigSurrogate): ListConfig = + ListConfig(surrogate.enabled, surrogate.maxPlayersPerPage) } } diff --git a/src/main/kotlin/com/rose/gateway/config/schema/MinecraftConfig.kt b/src/main/kotlin/com/rose/gateway/config/schema/MinecraftConfig.kt index 3bd68590..52316543 100644 --- a/src/main/kotlin/com/rose/gateway/config/schema/MinecraftConfig.kt +++ b/src/main/kotlin/com/rose/gateway/config/schema/MinecraftConfig.kt @@ -7,20 +7,20 @@ import kotlinx.serialization.Serializable /** * Config options for Minecraft * - * @property primaryColor The color used for labels and Discord mentions in-game - * @property secondaryColor The color used for differentiated text elements and names of Discord users in-game - * @property tertiaryColor The color used for labelling config paths - * @property warningColor The color used for marking problems and config items that can be null + * @property primaryColor Used to indicate success, color headers, and mark Discord mentions in-game + * @property secondaryColor Used to indicate information, differentiate text, and mark names of Discord users in-game + * @property tertiaryColor Used to indicate configuration paths and mark less important info + * @property warningColor Used to indicate errors/warnings and mark configurations that can be null * @constructor Create empty Minecraft config */ @Serializable data class MinecraftConfig( - @ConfigItem("Used for labels and Discord mentions in-game.") + @ConfigItem("Used to indicate success, color headers, and mark Discord mentions in-game") var primaryColor: String, - @ConfigItem("Used for differentiated text elements and names of Discord users in-game.") + @ConfigItem("Used to indicate information, differentiate text, and mark names of Discord users in-game") var secondaryColor: String, - @ConfigItem("Used for labelling config paths.") + @ConfigItem("Used to indicate configuration paths and mark less important info") var tertiaryColor: String, - @ConfigItem("Used for marking problems and config items that can be null.") + @ConfigItem("Used to indicate errors/warnings and mark configurations that can be null") var warningColor: String ) : ConfigObject diff --git a/src/main/kotlin/com/rose/gateway/config/schema/WhitelistConfig.kt b/src/main/kotlin/com/rose/gateway/config/schema/WhitelistConfig.kt index 8b0cf355..905ec03f 100644 --- a/src/main/kotlin/com/rose/gateway/config/schema/WhitelistConfig.kt +++ b/src/main/kotlin/com/rose/gateway/config/schema/WhitelistConfig.kt @@ -1,6 +1,7 @@ package com.rose.gateway.config.schema import com.rose.gateway.config.markers.CommonExtensionConfig +import com.rose.gateway.config.markers.ConfigItem import com.rose.gateway.discord.bot.extensions.whitelist.WhitelistExtension import com.rose.gateway.shared.serialization.SurrogateBasedSerializer import com.rose.gateway.shared.serialization.SurrogateConverter @@ -9,30 +10,38 @@ import kotlinx.serialization.Serializable /** * Config options for the "whitelist extension" * + * @property maxPlayersPerPage The maximum number of player allowed per list page * @constructor Creates a "whitelist config" with the provided data * * @param enabled Whether the extension is enabled */ @Serializable(with = WhitelistConfigSerializer::class) class WhitelistConfig( - enabled: Boolean + enabled: Boolean, + /** + * The maximum number of player allowed per list page + */ + @ConfigItem("The maximum number of player allowed per list page") var maxPlayersPerPage: Int ) : CommonExtensionConfig(enabled, WhitelistExtension.extensionName()) /** * Surrogate for serialization of [WhitelistConfig] * * @property enabled Whether the extension is enabled + * @property maxPlayersPerPage The maximum number of player allowed per list page * @constructor Create a "whitelist config" surrogate with the provided data * * @see WhitelistConfig * @see WhitelistConfigSerializer */ @Serializable -data class WhitelistConfigSurrogate(val enabled: Boolean) { +data class WhitelistConfigSurrogate(val enabled: Boolean, val maxPlayersPerPage: Int) { companion object : SurrogateConverter { - override fun fromBase(base: WhitelistConfig): WhitelistConfigSurrogate = WhitelistConfigSurrogate(base.enabled) + override fun fromBase(base: WhitelistConfig): WhitelistConfigSurrogate = + WhitelistConfigSurrogate(base.enabled, base.maxPlayersPerPage) - override fun toBase(surrogate: WhitelistConfigSurrogate): WhitelistConfig = WhitelistConfig(surrogate.enabled) + override fun toBase(surrogate: WhitelistConfigSurrogate): WhitelistConfig = + WhitelistConfig(surrogate.enabled, surrogate.maxPlayersPerPage) } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/BotContext.kt b/src/main/kotlin/com/rose/gateway/discord/bot/BotContext.kt index 30c734a6..8664ada0 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/BotContext.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/BotContext.kt @@ -1,7 +1,7 @@ package com.rose.gateway.discord.bot import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.botChannels +import com.rose.gateway.config.access.botChannels import com.rose.gateway.discord.bot.client.ClientInfo import dev.kord.core.entity.Guild import dev.kord.core.entity.channel.TextChannel diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/DiscordBot.kt b/src/main/kotlin/com/rose/gateway/discord/bot/DiscordBot.kt index 1e2d2884..001e380c 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/DiscordBot.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/DiscordBot.kt @@ -4,7 +4,7 @@ import com.kotlindiscord.kord.extensions.ExtensibleBot import com.kotlindiscord.kord.extensions.utils.loadModule import com.rose.gateway.GatewayPlugin import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.botToken +import com.rose.gateway.config.access.botToken import com.rose.gateway.discord.bot.presence.BotPresence import com.rose.gateway.minecraft.logging.Logger import com.rose.gateway.shared.concurrency.PluginCoroutineScope diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/about/AboutExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/about/AboutExtension.kt index 37b8146b..4e22b818 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/about/AboutExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/about/AboutExtension.kt @@ -5,9 +5,12 @@ import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand import com.kotlindiscord.kord.extensions.types.respond import com.rose.gateway.GatewayPlugin import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.aboutExtensionEnabled +import com.rose.gateway.config.access.aboutExtensionEnabled +import com.rose.gateway.config.access.secondaryColor import com.rose.gateway.discord.bot.extensions.ExtensionToggle import com.rose.gateway.minecraft.logging.Logger +import dev.kord.common.Color +import dev.kord.rest.builder.message.create.embed import org.koin.core.component.inject /** @@ -33,28 +36,35 @@ class AboutExtension : Extension() { override suspend fun setup() { ephemeralSlashCommand { name = "version" - description = "Gives the current version of the Gateway plugin." + description = "Gives the current version of the Gateway plugin" action { Logger.info("${user.asUserOrNull()?.username} requested plugin version!") respond { - content = "I am currently version ${plugin.description.version}." + - "All versions are available at https://github.com/nicholasgrose/Gateway/." + embed { + title = plugin.description.version + description = "All versions are available at https://github.com/nicholasgrose/Gateway/releases." + color = Color(config.secondaryColor().value()) + } } } } ephemeralSlashCommand { name = "blockgod" - description = "Summon the block god for a moment." + description = "Summon the block god for but a moment" action { Logger.info("${user.asUserOrNull()?.username} used the super secret command!") respond { - @Suppress("HttpUrlsUsage") - content = "http://www.scpwiki.com/church-of-the-broken-god-hub" + embed { + title = "Witness The Glory" + description = "The Block God must be experienced." + url = "https://scp-wiki.wikidot.com/church-of-the-broken-god-hub" + color = Color(config.secondaryColor().value()) + } } } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/ChatExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/ChatExtension.kt index e7cfca25..f4e30f1b 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/ChatExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/ChatExtension.kt @@ -3,7 +3,7 @@ package com.rose.gateway.discord.bot.extensions.chat import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.event import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.chatExtensionEnabled +import com.rose.gateway.config.access.chatExtensionEnabled import com.rose.gateway.discord.bot.checks.MessageCheck import com.rose.gateway.discord.bot.extensions.ExtensionToggle import com.rose.gateway.discord.bot.extensions.chat.processing.DiscordMessageProcessor diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/DiscordMessageProcessor.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/DiscordMessageProcessor.kt index 54a4612e..e707e428 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/DiscordMessageProcessor.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/DiscordMessageProcessor.kt @@ -1,7 +1,7 @@ package com.rose.gateway.discord.bot.extensions.chat.processing import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.secondaryColor +import com.rose.gateway.config.access.secondaryColor import com.rose.gateway.minecraft.component.atMember import com.rose.gateway.minecraft.component.component import com.rose.gateway.minecraft.component.italic diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/UserMentionTokenProcessor.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/UserMentionTokenProcessor.kt index 40b4178b..7ef40398 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/UserMentionTokenProcessor.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/chat/processing/UserMentionTokenProcessor.kt @@ -1,7 +1,7 @@ package com.rose.gateway.discord.bot.extensions.chat.processing import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.primaryColor +import com.rose.gateway.config.access.primaryColor import com.rose.gateway.minecraft.component.atMember import com.rose.gateway.minecraft.component.component import com.rose.gateway.shared.parsing.TokenProcessor diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/ip/IpExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/ip/IpExtension.kt index f85fb2ad..681d642b 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/ip/IpExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/ip/IpExtension.kt @@ -4,10 +4,13 @@ import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand import com.kotlindiscord.kord.extensions.types.respond import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.displayIp -import com.rose.gateway.config.extensions.ipExtensionEnabled +import com.rose.gateway.config.access.displayIp +import com.rose.gateway.config.access.ipExtensionEnabled +import com.rose.gateway.config.access.secondaryColor import com.rose.gateway.discord.bot.extensions.ExtensionToggle import com.rose.gateway.minecraft.logging.Logger +import dev.kord.common.Color +import dev.kord.rest.builder.message.create.embed import org.koin.core.component.inject /** @@ -31,13 +34,17 @@ class IpExtension : Extension() { override suspend fun setup() { ephemeralSlashCommand { name = "ip" - description = "Displays the server IP." + description = "Displays the server IP" action { Logger.info("${user.asUserOrNull()?.username} requested server IP!") respond { - content = "Current IP: ```${config.displayIp()}```" + embed { + title = "Current IP" + description = "```${config.displayIp()}```" + color = Color(config.secondaryColor().value()) + } } } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/list/ListExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/list/ListExtension.kt index 7e5e1668..5a547545 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/list/ListExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/list/ListExtension.kt @@ -2,12 +2,18 @@ package com.rose.gateway.discord.bot.extensions.list import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand -import com.kotlindiscord.kord.extensions.types.respond import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.listExtensionEnabled +import com.rose.gateway.config.access.listExtensionEnabled +import com.rose.gateway.config.access.maxPlayersPerListPage +import com.rose.gateway.config.access.secondaryColor import com.rose.gateway.discord.bot.extensions.ExtensionToggle +import com.rose.gateway.discord.bot.message.groupAndPaginateItems +import com.rose.gateway.discord.text.discordBoldSafe import com.rose.gateway.minecraft.logging.Logger import com.rose.gateway.minecraft.server.ServerInfo +import com.rose.gateway.shared.text.plurality +import dev.kord.common.Color +import dev.kord.rest.builder.message.create.embed import org.koin.core.component.inject /** @@ -31,21 +37,36 @@ class ListExtension : Extension() { override suspend fun setup() { ephemeralSlashCommand { name = "list" - description = "Lists all online players." + description = "Lists all online players" action { Logger.info("${user.asUserOrNull()?.username} requested player list!") - val playerList = ServerInfo.onlinePlayers - val response = if (playerList.isEmpty()) "No players online." else { - val playerListString = ServerInfo.onlinePlayers.joinToString(", ") { player -> player.name } + val maxPlayersPerPage = config.maxPlayersPerListPage() + val onlinePlayers = ServerInfo.onlinePlayers + val onlinePlayerCount = onlinePlayers.size - "Players online: $playerListString" - } + groupAndPaginateItems(onlinePlayers, maxPlayersPerPage, { + embed { + title = "0 Online Players" + description = "No players online." + color = Color(config.secondaryColor().value()) + } + }, { groupIndex, group -> + page { + title = plurality( + onlinePlayerCount, + "1 Online Player", + "$onlinePlayerCount Player Online" + ) + description = group.withIndex().joinToString("\n") { (playerIndex, player) -> + val playerNumber = (groupIndex * maxPlayersPerPage) + playerIndex + 1 - respond { - content = response - } + "**$playerNumber.** ${player.name.discordBoldSafe()}" + } + color = Color(config.secondaryColor().value()) + } + }) } } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/tps/TpsExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/tps/TpsExtension.kt index a1a39857..2f660752 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/tps/TpsExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/tps/TpsExtension.kt @@ -4,9 +4,12 @@ import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand import com.kotlindiscord.kord.extensions.types.respond import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.tpsExtensionEnabled +import com.rose.gateway.config.access.secondaryColor +import com.rose.gateway.config.access.tpsExtensionEnabled import com.rose.gateway.discord.bot.extensions.ExtensionToggle import com.rose.gateway.minecraft.server.ServerInfo +import dev.kord.common.Color +import dev.kord.rest.builder.message.create.embed import org.koin.core.component.inject import kotlin.math.roundToInt @@ -31,18 +34,21 @@ class TpsExtension : Extension() { override suspend fun setup() { ephemeralSlashCommand { name = "tps" - description = "Queries the server for its current TPS." + description = "Queries the server for its current TPS" action { val tps = ServerInfo.tps respond { - content = """ - TPS (ticks/sec): - **1m:** ${tps.oneMinute.roundToInt()} t/s - **5m:** ${tps.fiveMinute.roundToInt()} t/s - **15m:** ${tps.fifteenMinute.roundToInt()} t/s - """.trimIndent() + embed { + title = "TPS (ticks/sec)" + description = """ + **1m:** ${tps.oneMinute.roundToInt()} t/s + **5m:** ${tps.fiveMinute.roundToInt()} t/s + **15m:** ${tps.fifteenMinute.roundToInt()} t/s + """.trimIndent() + color = Color(config.secondaryColor().value()) + } } } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/whitelist/WhitelistExtension.kt b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/whitelist/WhitelistExtension.kt index 9969af80..a635112a 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/extensions/whitelist/WhitelistExtension.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/extensions/whitelist/WhitelistExtension.kt @@ -1,15 +1,25 @@ package com.rose.gateway.discord.bot.extensions.whitelist +import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommandContext import com.kotlindiscord.kord.extensions.commands.application.slash.ephemeralSubCommand import com.kotlindiscord.kord.extensions.extensions.Extension import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand import com.kotlindiscord.kord.extensions.types.respond import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.whitelistExtensionEnabled +import com.rose.gateway.config.access.maxPlayersPerWhitelistPage +import com.rose.gateway.config.access.primaryColor +import com.rose.gateway.config.access.secondaryColor +import com.rose.gateway.config.access.warningColor +import com.rose.gateway.config.access.whitelistExtensionEnabled import com.rose.gateway.discord.bot.extensions.ExtensionToggle +import com.rose.gateway.discord.bot.message.groupAndPaginateItems +import com.rose.gateway.discord.text.discordBoldSafe import com.rose.gateway.minecraft.logging.Logger import com.rose.gateway.minecraft.whitelist.Whitelist import com.rose.gateway.minecraft.whitelist.WhitelistState +import com.rose.gateway.shared.text.plurality +import dev.kord.common.Color +import dev.kord.rest.builder.message.create.embed import org.koin.core.component.inject /** @@ -37,60 +47,119 @@ class WhitelistExtension : Extension() { ephemeralSubCommand(::WhitelistArguments) { name = "add" - description = "Adds a player to the whitelist." + description = "Adds a player to the whitelist" action { Logger.info("${user.asUserOrNull()?.username} added ${arguments.username} to whitelist!") - val status = when (Whitelist.addPlayer(arguments.username)) { - WhitelistState.STATE_MODIFIED -> "${arguments.username} successfully added to whitelist." - WhitelistState.STATE_SUSTAINED -> "${arguments.username} already exists in whitelist." - WhitelistState.STATE_INVALID -> "An error occurred adding ${arguments.username} to whitelist." - } + val state = Whitelist.addPlayer(arguments.username) - respond { - content = status - } + this.whitelistAddResponse(state) } } ephemeralSubCommand(::WhitelistArguments) { name = "remove" - description = "Removes a player from the whitelist." + description = "Removes a player from the whitelist" action { Logger.info("${user.asUserOrNull()?.username} removed ${arguments.username} from whitelist!") - val status = when (Whitelist.removePlayer(arguments.username)) { - WhitelistState.STATE_MODIFIED -> "${arguments.username} successfully removed from whitelist." - WhitelistState.STATE_SUSTAINED -> "${arguments.username} does not exist in whitelist." - WhitelistState.STATE_INVALID -> "Error occurred removing ${arguments.username} from whitelist." - } + val state = Whitelist.removePlayer(arguments.username) - respond { - content = status - } + whitelistRemoveResponse(state) } } ephemeralSubCommand { name = "list" - description = "Lists all currently whitelisted players." + description = "Lists all currently whitelisted players" action { Logger.info("${user.asUserOrNull()?.username} requested list of whitelisted players!") + val maxPlayersPerPage = config.maxPlayersPerWhitelistPage() val whitelistedPlayers = Whitelist.players - val response = if (whitelistedPlayers.isEmpty()) "No players currently whitelisted." - else { - val whiteListedPlayerString = whitelistedPlayers.map { it.name }.joinToString(separator = ", ") + val whitelistedPlayerCount = whitelistedPlayers.size + + groupAndPaginateItems(whitelistedPlayers, maxPlayersPerPage, { + embed { + title = "0 Whitelisted Players" + description = "No players in whitelist." + color = Color(config.secondaryColor().value()) + } + }, { groupIndex, group -> + page { + title = plurality( + whitelistedPlayerCount, + "1 Whitelisted Player", + "$whitelistedPlayerCount Whitelisted Players" + ) + description = group.withIndex().joinToString("\n") { (playerIndex, player) -> + val playerNumber = (groupIndex * maxPlayersPerPage) + playerIndex + 1 + val playerName = player.name?.discordBoldSafe() ?: player.uniqueId + + "**$playerNumber.** $playerName" + } + color = Color(config.secondaryColor().value()) + } + }) + } + } + } + } + + private suspend fun EphemeralSlashCommandContext.whitelistAddResponse( + state: WhitelistState + ) { + respond { + when (state) { + WhitelistState.STATE_MODIFIED -> embed { + title = "Whitelist Changed" + description = + "**${arguments.username.discordBoldSafe()}** successfully added to whitelist." + color = Color(WhitelistExtension.config.primaryColor().value()) + } + + WhitelistState.STATE_SUSTAINED -> embed { + title = "Whitelist Not Changed" + description = "**${arguments.username.discordBoldSafe()}** already exists in whitelist." + color = Color(WhitelistExtension.config.secondaryColor().value()) + } - "Players currently whitelisted: $whiteListedPlayerString" - } + WhitelistState.STATE_INVALID -> embed { + title = "Whitelist Addition Failed" + description = + "An error occurred adding **${arguments.username.discordBoldSafe()}** to whitelist." + color = Color(WhitelistExtension.config.warningColor().value()) + } + } + } + } + + private suspend fun EphemeralSlashCommandContext.whitelistRemoveResponse( + state: WhitelistState + ) { + respond { + when (state) { + WhitelistState.STATE_MODIFIED -> embed { + title = "Whitelist Changed" + description = + "**${arguments.username.discordBoldSafe()}** successfully removed from whitelist." + color = Color(WhitelistExtension.config.primaryColor().value()) + } + + WhitelistState.STATE_SUSTAINED -> embed { + title = "Whitelist Not Changed" + description = "**${arguments.username.discordBoldSafe()}** does not exist in whitelist." + color = Color(WhitelistExtension.config.secondaryColor().value()) + } - respond { - content = response - } + WhitelistState.STATE_INVALID -> embed { + title = "Whitelist Removal Failed" + description = "Error occurred removing" + + " **${arguments.username.discordBoldSafe()}** from whitelist." + color = Color(WhitelistExtension.config.warningColor().value()) } } } diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/message/GroupAndPaginateItems.kt b/src/main/kotlin/com/rose/gateway/discord/bot/message/GroupAndPaginateItems.kt new file mode 100644 index 00000000..5d705413 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/discord/bot/message/GroupAndPaginateItems.kt @@ -0,0 +1,45 @@ +package com.rose.gateway.discord.bot.message + +import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommandContext +import com.kotlindiscord.kord.extensions.pagination.builders.PaginatorBuilder +import com.kotlindiscord.kord.extensions.types.editingPaginator +import com.kotlindiscord.kord.extensions.types.respond +import com.rose.gateway.shared.collections.group +import dev.kord.rest.builder.message.create.FollowupMessageCreateBuilder + +/** + * Groups the items of a collection and then displays them in a paginator if any exist + * + * @param T The type of the items to display + * @param displayItems The items to display + * @param pageGroupSize The size of groups to create from the display items + * @param noItemsResponseBuilder Builder that runs if there are no display items + * @param groupPageBuilder Builder that runs for each group of display items + * @receiver Response builder + * @receiver Editing paginator builder + * + * @see group + * @see editingPaginator + */ +suspend fun EphemeralSlashCommandContext<*, *>.groupAndPaginateItems( + displayItems: Collection, + pageGroupSize: Int, + noItemsResponseBuilder: FollowupMessageCreateBuilder.() -> Unit, + groupPageBuilder: PaginatorBuilder.(Int, List) -> Unit +) { + if (displayItems.isEmpty()) { + respond { + noItemsResponseBuilder() + } + + return + } + + val groupings = displayItems.group(pageGroupSize) + + editingPaginator { + for ((groupIndex, group) in groupings.withIndex()) { + groupPageBuilder(groupIndex, group) + } + }.send() +} diff --git a/src/main/kotlin/com/rose/gateway/discord/bot/presence/BotPresence.kt b/src/main/kotlin/com/rose/gateway/discord/bot/presence/BotPresence.kt index dee1bded..6de14330 100644 --- a/src/main/kotlin/com/rose/gateway/discord/bot/presence/BotPresence.kt +++ b/src/main/kotlin/com/rose/gateway/discord/bot/presence/BotPresence.kt @@ -2,6 +2,7 @@ package com.rose.gateway.discord.bot.presence import com.rose.gateway.discord.bot.DiscordBot import com.rose.gateway.minecraft.server.ServerInfo +import com.rose.gateway.shared.text.plurality import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -26,9 +27,12 @@ object BotPresence : KoinComponent { * @return The correct text for how many players there are */ fun presenceForPlayerCount(): String { - return when (val playerCount = ServerInfo.playerCount) { - 1 -> "Minecraft ($playerCount Player)" - else -> "Minecraft ($playerCount Players)" - } + val playerCount = ServerInfo.playerCount + + return plurality( + playerCount, + "Minecraft (1 Player)", + "Minecraft ($playerCount Players)" + ) } } diff --git a/src/main/kotlin/com/rose/gateway/minecraft/CommandRegistry.kt b/src/main/kotlin/com/rose/gateway/minecraft/CommandRegistry.kt index d316b966..f775d6c1 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/CommandRegistry.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/CommandRegistry.kt @@ -2,6 +2,7 @@ package com.rose.gateway.minecraft import com.rose.gateway.GatewayPlugin import com.rose.gateway.minecraft.commands.arguments.ConfigBooleanArgs +import com.rose.gateway.minecraft.commands.arguments.ConfigIntArgs import com.rose.gateway.minecraft.commands.arguments.ConfigItemArgs import com.rose.gateway.minecraft.commands.arguments.ConfigStringArgs import com.rose.gateway.minecraft.commands.arguments.addStringListConfigArgs @@ -54,6 +55,10 @@ object CommandRegistry : KoinComponent { ConfigCommands.setConfig(context) } + runner(::ConfigIntArgs) { context -> + ConfigCommands.setConfig(context) + } + runner(::ConfigStringArgs) { context -> ConfigCommands.setConfig(context) } diff --git a/src/main/kotlin/com/rose/gateway/minecraft/chat/ChatListener.kt b/src/main/kotlin/com/rose/gateway/minecraft/chat/ChatListener.kt index fea9fab1..72657557 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/chat/ChatListener.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/chat/ChatListener.kt @@ -1,7 +1,7 @@ package com.rose.gateway.minecraft.chat import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.chatExtensionEnabled +import com.rose.gateway.config.access.chatExtensionEnabled import com.rose.gateway.discord.bot.extensions.chat.GameChatEvent import com.rose.gateway.minecraft.chat.processing.discordMessage import com.rose.gateway.shared.concurrency.PluginCoroutineScope diff --git a/src/main/kotlin/com/rose/gateway/minecraft/chat/LaunchIfChatExtensionEnabled.kt b/src/main/kotlin/com/rose/gateway/minecraft/chat/LaunchIfChatExtensionEnabled.kt index 94f1d145..edf70e93 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/chat/LaunchIfChatExtensionEnabled.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/chat/LaunchIfChatExtensionEnabled.kt @@ -1,7 +1,7 @@ package com.rose.gateway.minecraft.chat import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.chatExtensionEnabled +import com.rose.gateway.config.access.chatExtensionEnabled import com.rose.gateway.shared.concurrency.PluginCoroutineScope import com.rose.gateway.shared.concurrency.launchIf import kotlinx.coroutines.CoroutineScope diff --git a/src/main/kotlin/com/rose/gateway/minecraft/chat/processing/tokens/result/MentionResult.kt b/src/main/kotlin/com/rose/gateway/minecraft/chat/processing/tokens/result/MentionResult.kt index 8178167c..5f5a2689 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/chat/processing/tokens/result/MentionResult.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/chat/processing/tokens/result/MentionResult.kt @@ -1,7 +1,7 @@ package com.rose.gateway.minecraft.chat.processing.tokens.result import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.primaryColor +import com.rose.gateway.config.access.primaryColor import com.rose.gateway.discord.bot.DiscordBot import com.rose.gateway.discord.bot.DiscordBotConstants import com.rose.gateway.minecraft.component.atMember diff --git a/src/main/kotlin/com/rose/gateway/minecraft/commands/arguments/ConfigArgs.kt b/src/main/kotlin/com/rose/gateway/minecraft/commands/arguments/ConfigArgs.kt index 39fd3143..490faad7 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/commands/arguments/ConfigArgs.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/commands/arguments/ConfigArgs.kt @@ -4,8 +4,10 @@ import com.rose.gateway.minecraft.commands.completers.ConfigCompleter import com.rose.gateway.minecraft.commands.framework.runner.ArgParser import com.rose.gateway.minecraft.commands.framework.runner.CommandArgs import com.rose.gateway.minecraft.commands.parsers.BooleanParser +import com.rose.gateway.minecraft.commands.parsers.IntParser import com.rose.gateway.minecraft.commands.parsers.StringParser import com.rose.gateway.minecraft.commands.parsers.boolean +import com.rose.gateway.minecraft.commands.parsers.int import com.rose.gateway.minecraft.commands.parsers.string import com.rose.gateway.minecraft.commands.parsers.typedConfigItem import kotlin.reflect.KType @@ -65,6 +67,21 @@ class ConfigBooleanArgs : ConfigArgs>( + typeOf(), + { + int { + name = "VALUE" + description = "Integer to use the item with." + } + } +) + /** * Config args for string values * diff --git a/src/main/kotlin/com/rose/gateway/minecraft/component/Color.kt b/src/main/kotlin/com/rose/gateway/minecraft/component/Color.kt index a72a6eb8..bd07e3ae 100644 --- a/src/main/kotlin/com/rose/gateway/minecraft/component/Color.kt +++ b/src/main/kotlin/com/rose/gateway/minecraft/component/Color.kt @@ -1,10 +1,10 @@ package com.rose.gateway.minecraft.component import com.rose.gateway.config.PluginConfig -import com.rose.gateway.config.extensions.primaryColor -import com.rose.gateway.config.extensions.secondaryColor -import com.rose.gateway.config.extensions.tertiaryColor -import com.rose.gateway.config.extensions.warningColor +import com.rose.gateway.config.access.primaryColor +import com.rose.gateway.config.access.secondaryColor +import com.rose.gateway.config.access.tertiaryColor +import com.rose.gateway.config.access.warningColor import net.kyori.adventure.text.Component import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/src/main/kotlin/com/rose/gateway/shared/collections/Group.kt b/src/main/kotlin/com/rose/gateway/shared/collections/Group.kt new file mode 100644 index 00000000..2eda13c2 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/shared/collections/Group.kt @@ -0,0 +1,24 @@ +package com.rose.gateway.shared.collections + +/** + * Groups the elements of a collection into lists of the given size + * + * @param T The type of the elements in the collection + * @param groupSize The size of the groups to create + * @return The resultant element groups + */ +fun Collection.group(groupSize: Int): List> { + val groups = mutableListOf>() + + for ((index, element) in this.withIndex()) { + if (index % groupSize == 0) { + groups.add(mutableListOf(element)) + continue + } + + val subsetIndex = index / groupSize + groups[subsetIndex].add(element) + } + + return groups +} diff --git a/src/main/kotlin/com/rose/gateway/shared/text/Plurality.kt b/src/main/kotlin/com/rose/gateway/shared/text/Plurality.kt new file mode 100644 index 00000000..3d83fc41 --- /dev/null +++ b/src/main/kotlin/com/rose/gateway/shared/text/Plurality.kt @@ -0,0 +1,14 @@ +package com.rose.gateway.shared.text + +/** + * Determines whether to use the singular or plural for a count + * + * @param count The number of items + * @param singular The singular form + * @param plural The plural form + * @return The form corresponding to the count + */ +fun plurality(count: Int, singular: String, plural: String): String = when (count) { + 1 -> singular + else -> plural +} diff --git a/src/main/resources/default_gateway_config.yaml b/src/main/resources/default_gateway_config.yaml index 837f7031..589d5cd3 100644 --- a/src/main/resources/default_gateway_config.yaml +++ b/src/main/resources/default_gateway_config.yaml @@ -1,50 +1,50 @@ -# Settings for how the Discord bot functions. +# Settings for how the Discord bot functions bot: - # The token used by the bot to access discord. Accessible at https://discord.com/developers/applications/. + # The token used by the bot to access discord. Accessible at https://discord.com/developers/applications/ token: PLACEHOLDER_BOT_TOKEN - # The names of the channels in which the bot should post/accept chat messages. + # The names of the channels in which the bot should post/accept chat messages botChannels: - speakers-of-the-gateway - # Configurations for the various bot extensions. + # Configurations for the various bot extensions extensions: - # Configurations for the about extension, which gives information about the plugin. + # Configurations for the about extension, which gives information about the plugin about: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true - # Configurations for the chat extension, which gives Minecraft chat integration. + # Configurations for the chat extension, which gives Minecraft chat integration chat: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true - # Configurations for the IP extension, which gives the IP command. + # Configurations for the IP extension, which gives the IP command ip: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true - # The IP that will be displayed by the IP command. + # The IP that will be displayed by the IP command displayIp: 'localhost:25565' - # Configurations for the list extension, which gives information about online players. + # Configurations for the list extension, which gives information about online players list: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true + # The maximum number of player allowed per list page + maxPlayersPerPage: 15 # Configurations for the tps extension, which allows the querying of server TPS data tps: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true - # Configurations for the whitelist extension, which gives commands that manipulate the whitelist. + # Configurations for the whitelist extension, which gives commands that manipulate the whitelist whitelist: - # Whether the extension is enabled. + # Whether the extension is enabled enabled: true + # The maximum number of player allowed per whitelist page + maxPlayersPerPage: 15 -# Settings for how the plugin functions on the Minecraft server. +# Settings for how the plugin functions on the Minecraft server minecraft: - - # Used for labels and Discord mentions in-game. + # Used to indicate success, color headers, and mark Discord mentions in-game primaryColor: '#56EE5C' - - # Used for differentiated text elements and names of Discord users in-game. + # Used to indicate information, differentiate text, and mark names of Discord users in-game secondaryColor: '#7289DA' - - # Used for labelling configuration paths. + # Used to indicate configuration paths and mark less important info tertiaryColor: '#F526ED' - - # Used for marking configurations that can be null. + # Used to indicate errors/warnings and mark configurations that can be null warningColor: '#EB4325'