Rewrite config to use Konf (4): Features
The entire config parsing is now rewritten. This entirely removes toml4j in favor of Konf. It also removes all remaining RawConfig logic.
This commit is contained in:
parent
bb03474bf5
commit
d6492bae8f
|
@ -36,7 +36,6 @@ val test by tasks.getting(Test::class) {
|
|||
dependencies {
|
||||
implementation("com.uchuhimo:konf-core:0.20.0")
|
||||
implementation("com.uchuhimo:konf-toml:0.20.0")
|
||||
implementation("com.moandjiezana.toml:toml4j:0.7.2")
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation("org.javacord:javacord:3.0.4")
|
||||
implementation("org.mapdb:mapdb:3.0.7")
|
||||
|
|
|
@ -3,7 +3,6 @@ package moe.kageru.kagebot
|
|||
import moe.kageru.kagebot.Util.checked
|
||||
import moe.kageru.kagebot.config.Config
|
||||
import moe.kageru.kagebot.config.ConfigParser
|
||||
import moe.kageru.kagebot.config.RawConfig
|
||||
import moe.kageru.kagebot.cron.CronD
|
||||
import moe.kageru.kagebot.persistence.Dao
|
||||
import org.javacord.api.DiscordApiBuilder
|
||||
|
@ -44,7 +43,7 @@ object Kagebot {
|
|||
val api = DiscordApiBuilder().setToken(secret).login().join()
|
||||
Globals.api = api
|
||||
try {
|
||||
ConfigParser.initialLoad(RawConfig.DEFAULT_CONFIG_PATH)
|
||||
ConfigParser.initialLoad(ConfigParser.DEFAULT_CONFIG_PATH)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
println("Config error:\n$e,\n${e.message},\n${e.stackTrace.joinToString("\n")}")
|
||||
exitProcess(1)
|
||||
|
|
|
@ -10,11 +10,15 @@ object Config {
|
|||
val systemSpec = Config { addSpec(SystemSpec) }.from.toml
|
||||
val localeSpec = Config { addSpec(LocalizationSpec) }.from.toml
|
||||
val commandSpec = Config { addSpec(CommandSpec) }.from.toml
|
||||
val featureSpec = Config { addSpec(FeatureSpec) }.from.toml
|
||||
|
||||
lateinit var system: Config
|
||||
lateinit var localization: Config
|
||||
lateinit var server: Server
|
||||
lateinit var commandConfig: Config
|
||||
lateinit var features: Features
|
||||
lateinit var featureConfig: Config
|
||||
lateinit var server: Server
|
||||
|
||||
// for easier access
|
||||
val features: Features get() = featureConfig[FeatureSpec.features]
|
||||
val commands: List<Command> get() = commandConfig[CommandSpec.command]
|
||||
}
|
||||
|
|
|
@ -2,26 +2,29 @@ package moe.kageru.kagebot.config
|
|||
|
||||
import moe.kageru.kagebot.Globals
|
||||
import moe.kageru.kagebot.config.SystemSpec.serverId
|
||||
import moe.kageru.kagebot.features.Features
|
||||
import java.io.File
|
||||
|
||||
object ConfigParser {
|
||||
val configFile: File = File(RawConfig.DEFAULT_CONFIG_PATH)
|
||||
internal const val DEFAULT_CONFIG_PATH = "config.toml"
|
||||
val configFile: File = File(DEFAULT_CONFIG_PATH)
|
||||
|
||||
fun initialLoad(file: String) {
|
||||
val rawConfig = RawConfig.read(file)
|
||||
val configFile = RawConfig.getFile(file)
|
||||
val configFile = getFile(file)
|
||||
val config = Config.systemSpec.file(configFile)
|
||||
Config.system = config
|
||||
Config.server = Globals.api.getServerById(config[serverId])
|
||||
.orElseThrow { IllegalArgumentException("Invalid server configured.") }
|
||||
Config.localization = Config.localeSpec.file(configFile)
|
||||
reloadFeatures(rawConfig)
|
||||
Config.featureConfig = Config.featureSpec.file(configFile)
|
||||
Config.commandConfig = Config.commandSpec.file(configFile)
|
||||
}
|
||||
|
||||
fun reloadFeatures(rawConfig: RawConfig) {
|
||||
Config.features = rawConfig.features?.let(::Features)
|
||||
?: Features(RawFeatures(null, null))
|
||||
private fun getFile(path: String): File {
|
||||
val file = File(path)
|
||||
if (file.isFile) {
|
||||
return file
|
||||
}
|
||||
println("Config not found, falling back to defaults...")
|
||||
return File(this::class.java.classLoader.getResource(path)!!.toURI())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package moe.kageru.kagebot.config
|
||||
|
||||
import com.uchuhimo.konf.ConfigSpec
|
||||
import moe.kageru.kagebot.command.Command
|
||||
import moe.kageru.kagebot.config.Config.system
|
||||
import moe.kageru.kagebot.features.Features
|
||||
import java.awt.Color
|
||||
|
||||
object SystemSpec : ConfigSpec() {
|
||||
private val rawColor by optional("#1793d0", name = "color")
|
||||
val serverId by required<String>()
|
||||
val color by kotlin.lazy { Color.decode(system[rawColor])!! }
|
||||
}
|
||||
|
||||
object LocalizationSpec : ConfigSpec() {
|
||||
val permissionDenied by optional("You do not have the permission to use this command.")
|
||||
val redirectedMessage by optional("says")
|
||||
val messageDeleted by optional("Your message was deleted.")
|
||||
val timeout by optional("You have been timed out for @@ minutes.")
|
||||
}
|
||||
|
||||
object CommandSpec : ConfigSpec(prefix = "") {
|
||||
val command by optional(emptyList<Command>())
|
||||
}
|
||||
|
||||
object FeatureSpec : ConfigSpec(prefix = "") {
|
||||
val features by optional(Features.DEFAULT, name = "feature")
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package moe.kageru.kagebot.config
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.moandjiezana.toml.Toml
|
||||
import com.uchuhimo.konf.ConfigSpec
|
||||
import moe.kageru.kagebot.command.Command
|
||||
import moe.kageru.kagebot.config.Config.system
|
||||
import java.awt.Color
|
||||
import java.io.File
|
||||
|
||||
class RawConfig(@SerializedName("feature") val features: RawFeatures?) {
|
||||
companion object {
|
||||
const val DEFAULT_CONFIG_PATH = "config.toml"
|
||||
|
||||
fun readFromString(tomlContent: String): RawConfig = Toml().read(tomlContent).to(RawConfig::class.java)
|
||||
|
||||
fun getFile(path: String): File {
|
||||
val file = File(path)
|
||||
if (file.isFile) {
|
||||
return file
|
||||
}
|
||||
println("Config not found, falling back to defaults...")
|
||||
return File(this::class.java.classLoader.getResource(path)!!.toURI())
|
||||
}
|
||||
|
||||
fun read(path: String = DEFAULT_CONFIG_PATH): RawConfig {
|
||||
val toml: Toml = Toml().read(getFile(path))
|
||||
return toml.to(RawConfig::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SystemSpec : ConfigSpec() {
|
||||
private val rawColor by optional("#1793d0", name = "color")
|
||||
val serverId by required<String>()
|
||||
val color by kotlin.lazy { Color.decode(system[rawColor])!! }
|
||||
}
|
||||
|
||||
object LocalizationSpec : ConfigSpec() {
|
||||
val permissionDenied by optional("You do not have the permission to use this command.")
|
||||
val redirectedMessage by optional("says")
|
||||
val messageDeleted by optional("Your message was deleted.")
|
||||
val timeout by optional("You have been timed out for @@ minutes.")
|
||||
}
|
||||
|
||||
object CommandSpec : ConfigSpec(prefix = "") {
|
||||
val command by optional(emptyList<Command>())
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package moe.kageru.kagebot.config
|
||||
|
||||
class RawFeatures(val welcome: RawWelcomeFeature?, val timeout: RawTimeoutFeature?)
|
||||
class RawWelcomeFeature(val content: List<String>?, val fallbackChannel: String?, val fallbackMessage: String?)
|
||||
class RawTimeoutFeature(val role: String?)
|
|
@ -1,23 +1,10 @@
|
|||
package moe.kageru.kagebot.features
|
||||
|
||||
import moe.kageru.kagebot.config.RawFeatures
|
||||
|
||||
class Features(
|
||||
val welcome: WelcomeFeature?,
|
||||
debug: DebugFeature,
|
||||
help: HelpFeature,
|
||||
getConfig: GetConfigFeature,
|
||||
setConfig: SetConfigFeature,
|
||||
val timeout: TimeoutFeature?
|
||||
) {
|
||||
constructor(rawFeatures: RawFeatures) : this(
|
||||
rawFeatures.welcome?.let(::WelcomeFeature),
|
||||
DebugFeature(),
|
||||
HelpFeature(),
|
||||
GetConfigFeature(),
|
||||
SetConfigFeature(),
|
||||
rawFeatures.timeout?.let(::TimeoutFeature)
|
||||
)
|
||||
class Features(val welcome: WelcomeFeature?, val timeout: TimeoutFeature?) {
|
||||
private val debug = DebugFeature()
|
||||
private val help = HelpFeature()
|
||||
private val getConfig = GetConfigFeature()
|
||||
private val setConfig = SetConfigFeature()
|
||||
|
||||
private val all = listOf(welcome, debug, help, getConfig, setConfig, timeout)
|
||||
private val featureMap = mapOf(
|
||||
|
@ -31,4 +18,8 @@ class Features(
|
|||
|
||||
fun findByString(feature: String) = featureMap[feature]
|
||||
fun eventFeatures() = all.filterIsInstance<EventFeature>()
|
||||
|
||||
companion object {
|
||||
val DEFAULT = Features(null, null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package moe.kageru.kagebot.features
|
||||
|
||||
import moe.kageru.kagebot.Log
|
||||
import moe.kageru.kagebot.MessageUtil.sendEmbed
|
||||
import moe.kageru.kagebot.config.Config
|
||||
import moe.kageru.kagebot.config.ConfigParser
|
||||
import moe.kageru.kagebot.config.RawConfig
|
||||
import org.javacord.api.entity.channel.TextChannel
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
class SetConfigFeature : MessageFeature {
|
||||
@ExperimentalStdlibApi
|
||||
|
@ -16,33 +14,16 @@ class SetConfigFeature : MessageFeature {
|
|||
return
|
||||
}
|
||||
val newConfig = message.messageAttachments[0].url.openStream().readAllBytes().decodeToString()
|
||||
val rawConfig = try {
|
||||
RawConfig.readFromString(newConfig)
|
||||
} catch (e: IllegalStateException) {
|
||||
reportError(message.channel, e)
|
||||
return
|
||||
}
|
||||
try {
|
||||
Config.localization = Config.localeSpec.string(newConfig)
|
||||
ConfigParser.reloadFeatures(rawConfig)
|
||||
Config.featureConfig = Config.featureSpec.string(newConfig)
|
||||
Config.commandConfig = Config.commandSpec.string(newConfig)
|
||||
ConfigParser.configFile.writeText(newConfig)
|
||||
message.channel.sendMessage("Config reloaded.")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (e: Exception) {
|
||||
message.channel.sendEmbed {
|
||||
addField("Error", "```${e.message}```")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportError(message: TextChannel, e: IllegalStateException) {
|
||||
message.sendEmbed {
|
||||
addField(
|
||||
"An unexpected error occured. This is probably caused by a malformed config file. Perhaps this can help:",
|
||||
"```$e: ${e.message}"
|
||||
)
|
||||
}
|
||||
Log.info("Could not parse new config: $e: ${e.message}")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package moe.kageru.kagebot.features
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import moe.kageru.kagebot.Log
|
||||
import moe.kageru.kagebot.MessageUtil.sendEmbed
|
||||
import moe.kageru.kagebot.Util.findRole
|
||||
|
@ -7,16 +8,14 @@ import moe.kageru.kagebot.Util.findUser
|
|||
import moe.kageru.kagebot.Util.ifNotEmpty
|
||||
import moe.kageru.kagebot.config.Config
|
||||
import moe.kageru.kagebot.config.LocalizationSpec
|
||||
import moe.kageru.kagebot.config.RawTimeoutFeature
|
||||
import moe.kageru.kagebot.persistence.Dao
|
||||
import org.javacord.api.entity.permission.Role
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
class TimeoutFeature(raw: RawTimeoutFeature) : MessageFeature {
|
||||
private val timeoutRole: Role = raw.role?.let(::findRole)
|
||||
?: throw IllegalArgumentException("No timeout role defined")
|
||||
class TimeoutFeature(@JsonProperty("role") role: String) : MessageFeature {
|
||||
private val timeoutRole: Role = findRole(role)
|
||||
|
||||
override fun handle(message: MessageCreateEvent) {
|
||||
val timeout = message.readableMessageContent.split(' ', limit = 4).let { args ->
|
||||
|
|
|
@ -5,14 +5,19 @@ import moe.kageru.kagebot.MessageUtil
|
|||
import moe.kageru.kagebot.Util
|
||||
import moe.kageru.kagebot.Util.checked
|
||||
import moe.kageru.kagebot.Util.failed
|
||||
import moe.kageru.kagebot.config.RawWelcomeFeature
|
||||
import org.javacord.api.DiscordApi
|
||||
import org.javacord.api.entity.channel.TextChannel
|
||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import org.javacord.api.event.server.member.ServerMemberJoinEvent
|
||||
|
||||
class WelcomeFeature(rawWelcome: RawWelcomeFeature) : MessageFeature, EventFeature {
|
||||
class WelcomeFeature(
|
||||
content: List<String>?,
|
||||
fallbackChannel: String?,
|
||||
private val fallbackMessage: String?
|
||||
) : MessageFeature, EventFeature {
|
||||
val embed: EmbedBuilder? by lazy { content?.let(MessageUtil::listToEmbed) }
|
||||
|
||||
override fun register(api: DiscordApi) {
|
||||
api.addServerMemberJoinListener { event ->
|
||||
checked { welcomeUser(event) }
|
||||
|
@ -40,14 +45,10 @@ class WelcomeFeature(rawWelcome: RawWelcomeFeature) : MessageFeature, EventFeatu
|
|||
|
||||
private fun hasFallback(): Boolean = fallbackChannel != null && fallbackMessage != null
|
||||
|
||||
val embed: EmbedBuilder? by lazy {
|
||||
rawWelcome.content?.let(MessageUtil::listToEmbed)
|
||||
}
|
||||
private val fallbackChannel: TextChannel? = rawWelcome.fallbackChannel?.let {
|
||||
requireNotNull(rawWelcome.fallbackMessage) {
|
||||
private val fallbackChannel: TextChannel? = fallbackChannel?.let {
|
||||
requireNotNull(fallbackMessage) {
|
||||
"[feature.welcome.fallbackMessage] must not be null if fallbackChannel is defined"
|
||||
}
|
||||
Util.findChannel(it)
|
||||
}
|
||||
private val fallbackMessage: String? = rawWelcome.fallbackMessage
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class ConfigTest : ShouldSpec({
|
|||
"should properly parse test config" {
|
||||
Config.system[SystemSpec.serverId] shouldNotBe null
|
||||
SystemSpec.color shouldBe Color.decode("#1793d0")
|
||||
Config.features shouldNotBe null
|
||||
Config.features.welcome!!.embed shouldNotBe null
|
||||
Config.commands.size shouldBe 3
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user