Rewrite config to use Konf (3): Commands
This commit is contained in:
parent
17c7120796
commit
bb03474bf5
@ -42,6 +42,7 @@ dependencies {
|
|||||||
implementation("org.mapdb:mapdb:3.0.7")
|
implementation("org.mapdb:mapdb:3.0.7")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.50")
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.50")
|
||||||
|
implementation("com.fasterxml.jackson.core:jackson-annotations:2.10.0.pr3")
|
||||||
|
|
||||||
testImplementation("io.kotlintest:kotlintest-runner-junit5:3.4.2")
|
testImplementation("io.kotlintest:kotlintest-runner-junit5:3.4.2")
|
||||||
testImplementation("io.mockk:mockk:1.9.3")
|
testImplementation("io.mockk:mockk:1.9.3")
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package moe.kageru.kagebot.command
|
package moe.kageru.kagebot.command
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import moe.kageru.kagebot.Globals
|
import moe.kageru.kagebot.Globals
|
||||||
import moe.kageru.kagebot.Log
|
import moe.kageru.kagebot.Log
|
||||||
import moe.kageru.kagebot.MessageUtil
|
import moe.kageru.kagebot.MessageUtil
|
||||||
import moe.kageru.kagebot.Util.applyIf
|
import moe.kageru.kagebot.Util.applyIf
|
||||||
import moe.kageru.kagebot.config.Config
|
import moe.kageru.kagebot.config.Config
|
||||||
import moe.kageru.kagebot.config.LocalizationSpec
|
import moe.kageru.kagebot.config.LocalizationSpec
|
||||||
import moe.kageru.kagebot.config.RawCommand
|
|
||||||
import moe.kageru.kagebot.features.MessageFeature
|
import moe.kageru.kagebot.features.MessageFeature
|
||||||
import org.javacord.api.entity.message.MessageAuthor
|
import org.javacord.api.entity.message.MessageAuthor
|
||||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||||
@ -14,29 +14,23 @@ import org.javacord.api.event.message.MessageCreateEvent
|
|||||||
|
|
||||||
private const val AUTHOR_PLACEHOLDER = "@@"
|
private const val AUTHOR_PLACEHOLDER = "@@"
|
||||||
|
|
||||||
class Command(cmd: RawCommand) {
|
class Command(
|
||||||
val trigger: String
|
val trigger: String,
|
||||||
private val response: String?
|
private val response: String? = null,
|
||||||
val matchType: MatchType
|
private val permissions: Permissions?,
|
||||||
private val permissions: Permissions?
|
@JsonProperty("action")
|
||||||
private val actions: MessageActions?
|
private val actions: MessageActions?,
|
||||||
val regex: Regex?
|
embed: List<String>?,
|
||||||
val embed: EmbedBuilder?
|
feature: String?,
|
||||||
val feature: MessageFeature?
|
matchType: String?
|
||||||
|
) {
|
||||||
init {
|
val matchType: MatchType = matchType?.let { type ->
|
||||||
trigger = cmd.trigger ?: throw IllegalArgumentException("Every command must have a trigger.")
|
MatchType.values().find { it.name.equals(type, ignoreCase = true) }
|
||||||
response = cmd.response
|
?: throw IllegalArgumentException("Invalid [command.matchType]: “$matchType”")
|
||||||
matchType = cmd.matchType?.let { type ->
|
} ?: MatchType.PREFIX
|
||||||
MatchType.values().find { it.name.equals(type, ignoreCase = true) }
|
val regex: Regex? = if (this.matchType == MatchType.REGEX) Regex(trigger) else null
|
||||||
?: throw IllegalArgumentException("Invalid [command.matchType]: “${cmd.matchType}”")
|
val embed: EmbedBuilder? = embed?.let(MessageUtil::listToEmbed)
|
||||||
} ?: MatchType.PREFIX
|
private val feature: MessageFeature? = feature?.let { Config.features.findByString(it) }
|
||||||
permissions = cmd.permissions?.let { Permissions(it) }
|
|
||||||
actions = cmd.actions?.let { MessageActions(it) }
|
|
||||||
regex = if (matchType == MatchType.REGEX) Regex(trigger) else null
|
|
||||||
embed = cmd.embed?.let(MessageUtil::listToEmbed)
|
|
||||||
feature = cmd.feature?.let { Config.features.findByString(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isAllowed(message: MessageCreateEvent) = permissions?.isAllowed(message) ?: true
|
fun isAllowed(message: MessageCreateEvent) = permissions?.isAllowed(message) ?: true
|
||||||
|
|
||||||
@ -69,6 +63,7 @@ class Command(cmd: RawCommand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
enum class MatchType {
|
enum class MatchType {
|
||||||
PREFIX {
|
PREFIX {
|
||||||
override fun matches(message: String, command: Command) = message.startsWith(command.trigger, ignoreCase = true)
|
override fun matches(message: String, command: Command) = message.startsWith(command.trigger, ignoreCase = true)
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
package moe.kageru.kagebot.command
|
package moe.kageru.kagebot.command
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import moe.kageru.kagebot.Log
|
import moe.kageru.kagebot.Log
|
||||||
import moe.kageru.kagebot.MessageUtil.sendEmbed
|
import moe.kageru.kagebot.MessageUtil.sendEmbed
|
||||||
import moe.kageru.kagebot.config.Config
|
import moe.kageru.kagebot.config.Config
|
||||||
import moe.kageru.kagebot.config.LocalizationSpec
|
import moe.kageru.kagebot.config.LocalizationSpec
|
||||||
import moe.kageru.kagebot.config.RawMessageActions
|
|
||||||
import org.javacord.api.event.message.MessageCreateEvent
|
import org.javacord.api.event.message.MessageCreateEvent
|
||||||
|
|
||||||
class MessageActions(rawActions: RawMessageActions) {
|
class MessageActions(
|
||||||
private val delete: Boolean = rawActions.delete
|
private val delete: Boolean = false,
|
||||||
private val redirect: MessageRedirect? = rawActions.redirect?.let(::MessageRedirect)
|
private val redirect: MessageRedirect?,
|
||||||
private val assignment: RoleAssignment? = rawActions.assign?.let(::RoleAssignment)
|
@JsonProperty("assign")
|
||||||
|
private val assignment: RoleAssignment?
|
||||||
|
) {
|
||||||
|
|
||||||
fun run(message: MessageCreateEvent, command: Command) {
|
fun run(message: MessageCreateEvent, command: Command) {
|
||||||
if (delete) {
|
if (delete) {
|
||||||
|
@ -7,14 +7,11 @@ import moe.kageru.kagebot.Util.applyIf
|
|||||||
import moe.kageru.kagebot.Util.failed
|
import moe.kageru.kagebot.Util.failed
|
||||||
import moe.kageru.kagebot.config.Config
|
import moe.kageru.kagebot.config.Config
|
||||||
import moe.kageru.kagebot.config.LocalizationSpec
|
import moe.kageru.kagebot.config.LocalizationSpec
|
||||||
import moe.kageru.kagebot.config.RawRedirect
|
|
||||||
import org.javacord.api.entity.channel.TextChannel
|
import org.javacord.api.entity.channel.TextChannel
|
||||||
import org.javacord.api.event.message.MessageCreateEvent
|
import org.javacord.api.event.message.MessageCreateEvent
|
||||||
|
|
||||||
internal class MessageRedirect(rawRedirect: RawRedirect) {
|
class MessageRedirect(target: String, private val anonymous: Boolean = false) {
|
||||||
private val target: TextChannel = rawRedirect.target?.let(Util::findChannel)
|
private val targetChannel: TextChannel = Util.findChannel(target)
|
||||||
?: throw IllegalArgumentException("Every redirect needs to have a target.")
|
|
||||||
private val anonymous: Boolean = rawRedirect.anonymous
|
|
||||||
|
|
||||||
fun execute(message: MessageCreateEvent, command: Command) {
|
fun execute(message: MessageCreateEvent, command: Command) {
|
||||||
val embed = MessageUtil.withEmbed {
|
val embed = MessageUtil.withEmbed {
|
||||||
@ -35,9 +32,9 @@ internal class MessageRedirect(rawRedirect: RawRedirect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MessageUtil.sendEmbed(target, embed).failed()) {
|
if (MessageUtil.sendEmbed(targetChannel, embed).failed()) {
|
||||||
target.sendMessage("Error: could not redirect message.")
|
targetChannel.sendMessage("Error: could not redirect message.")
|
||||||
Log.warn("Could not redirect message to channel $target")
|
Log.warn("Could not redirect message to channel $targetChannel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
package moe.kageru.kagebot.command
|
package moe.kageru.kagebot.command
|
||||||
|
|
||||||
import moe.kageru.kagebot.Util
|
import moe.kageru.kagebot.Util
|
||||||
import moe.kageru.kagebot.config.RawPermissions
|
|
||||||
import org.javacord.api.entity.permission.Role
|
import org.javacord.api.entity.permission.Role
|
||||||
import org.javacord.api.event.message.MessageCreateEvent
|
import org.javacord.api.event.message.MessageCreateEvent
|
||||||
|
|
||||||
class Permissions(perms: RawPermissions) {
|
class Permissions(
|
||||||
private val hasOneOf: Set<Role>?
|
hasOneOf: List<String>?,
|
||||||
private val hasNoneOf: Set<Role>?
|
hasNoneOf: List<String>?,
|
||||||
private val onlyDM: Boolean
|
private val onlyDM: Boolean = false
|
||||||
|
) {
|
||||||
init {
|
private val hasOneOf: Set<Role>? = hasOneOf?.mapTo(mutableSetOf(), Util::findRole)
|
||||||
hasOneOf = perms.hasOneOf?.mapTo(mutableSetOf(), Util::findRole)
|
private val hasNoneOf: Set<Role>? = hasNoneOf?.mapTo(mutableSetOf(), Util::findRole)
|
||||||
hasNoneOf = perms.hasNoneOf?.mapTo(mutableSetOf(), Util::findRole)
|
|
||||||
onlyDM = perms.onlyDM
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isAllowed(message: MessageCreateEvent): Boolean {
|
fun isAllowed(message: MessageCreateEvent): Boolean {
|
||||||
if (message.messageAuthor.isBotOwner) {
|
if (message.messageAuthor.isBotOwner) {
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package moe.kageru.kagebot.command
|
package moe.kageru.kagebot.command
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import moe.kageru.kagebot.Log
|
import moe.kageru.kagebot.Log
|
||||||
import moe.kageru.kagebot.Util
|
import moe.kageru.kagebot.Util
|
||||||
import moe.kageru.kagebot.Util.getUser
|
import moe.kageru.kagebot.Util.getUser
|
||||||
import moe.kageru.kagebot.config.RawAssignment
|
|
||||||
import org.javacord.api.event.message.MessageCreateEvent
|
import org.javacord.api.event.message.MessageCreateEvent
|
||||||
|
|
||||||
internal class RoleAssignment(rawAssignment: RawAssignment) {
|
class RoleAssignment(@JsonProperty("role") role: String) {
|
||||||
private val role = rawAssignment.role?.let { idOrName ->
|
private val role = Util.findRole(role)
|
||||||
Util.findRole(idOrName)
|
|
||||||
} ?: throw IllegalArgumentException("Can’t find role “${rawAssignment.role}”")
|
|
||||||
|
|
||||||
fun assign(message: MessageCreateEvent) =
|
fun assign(message: MessageCreateEvent) =
|
||||||
message.getUser()?.addRole(role, "Requested via command.")
|
message.getUser()?.addRole(role, "Requested via command.")
|
||||||
|
@ -9,9 +9,12 @@ import org.javacord.api.entity.server.Server
|
|||||||
object Config {
|
object Config {
|
||||||
val systemSpec = Config { addSpec(SystemSpec) }.from.toml
|
val systemSpec = Config { addSpec(SystemSpec) }.from.toml
|
||||||
val localeSpec = Config { addSpec(LocalizationSpec) }.from.toml
|
val localeSpec = Config { addSpec(LocalizationSpec) }.from.toml
|
||||||
|
val commandSpec = Config { addSpec(CommandSpec) }.from.toml
|
||||||
lateinit var system: Config
|
lateinit var system: Config
|
||||||
lateinit var localization: Config
|
lateinit var localization: Config
|
||||||
lateinit var server: Server
|
lateinit var server: Server
|
||||||
lateinit var commands: List<Command>
|
lateinit var commandConfig: Config
|
||||||
lateinit var features: Features
|
lateinit var features: Features
|
||||||
|
// for easier access
|
||||||
|
val commands: List<Command> get() = commandConfig[CommandSpec.command]
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package moe.kageru.kagebot.config
|
package moe.kageru.kagebot.config
|
||||||
|
|
||||||
import moe.kageru.kagebot.Globals
|
import moe.kageru.kagebot.Globals
|
||||||
import moe.kageru.kagebot.command.Command
|
|
||||||
import moe.kageru.kagebot.config.SystemSpec.serverId
|
import moe.kageru.kagebot.config.SystemSpec.serverId
|
||||||
import moe.kageru.kagebot.features.Features
|
import moe.kageru.kagebot.features.Features
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -18,12 +17,7 @@ object ConfigParser {
|
|||||||
.orElseThrow { IllegalArgumentException("Invalid server configured.") }
|
.orElseThrow { IllegalArgumentException("Invalid server configured.") }
|
||||||
Config.localization = Config.localeSpec.file(configFile)
|
Config.localization = Config.localeSpec.file(configFile)
|
||||||
reloadFeatures(rawConfig)
|
reloadFeatures(rawConfig)
|
||||||
reloadCommands(rawConfig)
|
Config.commandConfig = Config.commandSpec.file(configFile)
|
||||||
}
|
|
||||||
|
|
||||||
fun reloadCommands(rawConfig: RawConfig) {
|
|
||||||
Config.commands = rawConfig.commands?.map(::Command)?.toMutableList()
|
|
||||||
?: throw IllegalArgumentException("No commands found in config.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reloadFeatures(rawConfig: RawConfig) {
|
fun reloadFeatures(rawConfig: RawConfig) {
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package moe.kageru.kagebot.config
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
|
|
||||||
class RawCommand(
|
|
||||||
val trigger: String?,
|
|
||||||
val response: String?,
|
|
||||||
val matchType: String?,
|
|
||||||
val permissions: RawPermissions?,
|
|
||||||
@SerializedName("action")
|
|
||||||
val actions: RawMessageActions?,
|
|
||||||
val embed: List<String>?,
|
|
||||||
val feature: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
class RawPermissions(val hasOneOf: List<String>?, val hasNoneOf: List<String>?, val onlyDM: Boolean)
|
|
||||||
class RawMessageActions(val delete: Boolean, val redirect: RawRedirect?, val assign: RawAssignment?)
|
|
||||||
class RawRedirect(val target: String?, val anonymous: Boolean)
|
|
||||||
class RawAssignment(var role: String?)
|
|
@ -3,16 +3,12 @@ package moe.kageru.kagebot.config
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.moandjiezana.toml.Toml
|
import com.moandjiezana.toml.Toml
|
||||||
import com.uchuhimo.konf.ConfigSpec
|
import com.uchuhimo.konf.ConfigSpec
|
||||||
|
import moe.kageru.kagebot.command.Command
|
||||||
import moe.kageru.kagebot.config.Config.system
|
import moe.kageru.kagebot.config.Config.system
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class RawConfig(
|
class RawConfig(@SerializedName("feature") val features: RawFeatures?) {
|
||||||
@SerializedName("command")
|
|
||||||
val commands: List<RawCommand>?,
|
|
||||||
@SerializedName("feature")
|
|
||||||
val features: RawFeatures?
|
|
||||||
) {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_CONFIG_PATH = "config.toml"
|
const val DEFAULT_CONFIG_PATH = "config.toml"
|
||||||
|
|
||||||
@ -46,3 +42,7 @@ object LocalizationSpec : ConfigSpec() {
|
|||||||
val messageDeleted by optional("Your message was deleted.")
|
val messageDeleted by optional("Your message was deleted.")
|
||||||
val timeout by optional("You have been timed out for @@ minutes.")
|
val timeout by optional("You have been timed out for @@ minutes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object CommandSpec : ConfigSpec(prefix = "") {
|
||||||
|
val command by optional(emptyList<Command>())
|
||||||
|
}
|
||||||
|
@ -15,5 +15,4 @@ class HelpFeature : MessageFeature {
|
|||||||
|
|
||||||
private fun listCommands(message: MessageCreateEvent) = Config.commands
|
private fun listCommands(message: MessageCreateEvent) = Config.commands
|
||||||
.filter { it.matchType == MatchType.PREFIX && it.isAllowed(message) }
|
.filter { it.matchType == MatchType.PREFIX && it.isAllowed(message) }
|
||||||
.map { it.trigger }
|
.joinToString("\n") { it.trigger }
|
||||||
.joinToString("\n")
|
|
||||||
|
@ -25,7 +25,7 @@ class SetConfigFeature : MessageFeature {
|
|||||||
try {
|
try {
|
||||||
Config.localization = Config.localeSpec.string(newConfig)
|
Config.localization = Config.localeSpec.string(newConfig)
|
||||||
ConfigParser.reloadFeatures(rawConfig)
|
ConfigParser.reloadFeatures(rawConfig)
|
||||||
ConfigParser.reloadCommands(rawConfig)
|
Config.commandConfig = Config.commandSpec.string(newConfig)
|
||||||
ConfigParser.configFile.writeText(newConfig)
|
ConfigParser.configFile.writeText(newConfig)
|
||||||
message.channel.sendMessage("Config reloaded.")
|
message.channel.sendMessage("Config reloaded.")
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
@ -44,8 +44,8 @@ class WelcomeFeature(rawWelcome: RawWelcomeFeature) : MessageFeature, EventFeatu
|
|||||||
rawWelcome.content?.let(MessageUtil::listToEmbed)
|
rawWelcome.content?.let(MessageUtil::listToEmbed)
|
||||||
}
|
}
|
||||||
private val fallbackChannel: TextChannel? = rawWelcome.fallbackChannel?.let {
|
private val fallbackChannel: TextChannel? = rawWelcome.fallbackChannel?.let {
|
||||||
if (rawWelcome.fallbackMessage == null) {
|
requireNotNull(rawWelcome.fallbackMessage) {
|
||||||
throw IllegalArgumentException("[feature.welcome.fallbackMessage] must not be null if fallbackChannel is defined")
|
"[feature.welcome.fallbackMessage] must not be null if fallbackChannel is defined"
|
||||||
}
|
}
|
||||||
Util.findChannel(it)
|
Util.findChannel(it)
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,6 @@ class ConfigTest : ShouldSpec({
|
|||||||
redirectedMessage = "says"
|
redirectedMessage = "says"
|
||||||
messageDeleted = "dongered"
|
messageDeleted = "dongered"
|
||||||
timeout = "timeout"
|
timeout = "timeout"
|
||||||
|
|
||||||
[[command]]
|
|
||||||
response = "this command is broken"
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val message = TestUtil.mockMessage("anything")
|
val message = TestUtil.mockMessage("anything")
|
||||||
every { message.messageAttachments } returns listOf(mockk {
|
every { message.messageAttachments } returns listOf(mockk {
|
||||||
|
@ -10,7 +10,6 @@ import io.mockk.mockk
|
|||||||
import moe.kageru.kagebot.Kagebot.process
|
import moe.kageru.kagebot.Kagebot.process
|
||||||
import moe.kageru.kagebot.config.Config
|
import moe.kageru.kagebot.config.Config
|
||||||
import moe.kageru.kagebot.config.ConfigParser
|
import moe.kageru.kagebot.config.ConfigParser
|
||||||
import moe.kageru.kagebot.config.RawConfig
|
|
||||||
import org.javacord.api.DiscordApi
|
import org.javacord.api.DiscordApi
|
||||||
import org.javacord.api.entity.channel.ServerTextChannel
|
import org.javacord.api.entity.channel.ServerTextChannel
|
||||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||||
@ -22,7 +21,7 @@ import java.io.File
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object TestUtil {
|
object TestUtil {
|
||||||
val TIMEOUT_ROLE = mockk<Role> {
|
private val TIMEOUT_ROLE = mockk<Role> {
|
||||||
every { id } returns 123
|
every { id } returns 123
|
||||||
}
|
}
|
||||||
val TEST_ROLE = mockk<Role> {
|
val TEST_ROLE = mockk<Role> {
|
||||||
@ -117,11 +116,10 @@ object TestUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun withCommands(config: String, test: (() -> Unit)) {
|
fun withCommands(config: String, test: (() -> Unit)) {
|
||||||
val oldCmds = Config.commands
|
val oldCmds = Config.commandConfig
|
||||||
val rawConfig = RawConfig.readFromString(config)
|
Config.commandConfig = Config.commandSpec.string(config)
|
||||||
ConfigParser.reloadCommands(rawConfig)
|
|
||||||
test()
|
test()
|
||||||
Config.commands = oldCmds
|
Config.commandConfig = oldCmds
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withLocalization(config: String, test: (() -> Unit)) {
|
fun withLocalization(config: String, test: (() -> Unit)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user