refactor: Prepare Arguments and stuff

This commit is contained in:
Ahmad Ansori Palembani 2025-05-20 19:47:52 +07:00
parent 1c20e05066
commit c901eb2f66
16 changed files with 189 additions and 27 deletions

View file

@ -1,6 +1,6 @@
package io.github.null2264.tsukumogami.bot.core.module
import io.github.null2264.tsukumogami.core.module.annotation.Command
import io.github.null2264.tsukumogami.core.commands.annotation.Command
import io.github.null2264.tsukumogami.core.Context
import io.github.null2264.tsukumogami.core.module.BotModule

View file

@ -1,7 +1,7 @@
package io.github.null2264.tsukumogami.bot.core.module
import dev.kord.core.entity.effectiveName
import io.github.null2264.tsukumogami.core.module.annotation.Command
import io.github.null2264.tsukumogami.core.commands.annotation.Command
import io.github.null2264.tsukumogami.core.Context
import io.github.null2264.tsukumogami.core.module.BotModule
import kotlinx.datetime.Clock

View file

@ -0,0 +1,8 @@
package io.github.null2264.tsukumogami.bot.core.module.arguments
import io.github.null2264.tsukumogami.core.commands.Arguments
import io.github.null2264.tsukumogami.core.commands.ext.string
class TestArguments : Arguments() {
val test by string("Test")
}

View file

@ -11,7 +11,8 @@ import dev.kord.gateway.PrivilegedIntent
import io.github.null2264.tsukumogami.core.exceptions.CommandException
import io.github.null2264.tsukumogami.core.exceptions.CommandNotFound
import io.github.null2264.tsukumogami.core.module.BotModule
import io.github.null2264.tsukumogami.core.module.CommandHolder
import io.github.null2264.tsukumogami.core.commands.CommandHolder
import io.github.null2264.tsukumogami.core.utils.parseCommandAndArguments
import kotlin.reflect.full.callSuspend
import kotlinx.coroutines.runBlocking
@ -22,13 +23,13 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) {
private val prefixes: List<String>
private val client: Kord
// TODO: Bind Bot and Kord to Koin
init {
val currentConfig = BotConfigurator()
currentConfig.apply(configurator)
extensions = mutableMapOf()
currentConfig.extensions.forEach {
val module = it.call()
currentConfig.extensions.forEach { module ->
module.setup()
module.install(this, currentConfig)
extensions[module.name] = module
@ -59,7 +60,7 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) {
fun getCommand(name: String) = commands[name]
private fun getContext(message: Message): Context {
val candidate = message.content.hasPrefix()
val candidate = message.content.parsePrefixCommandAndArguments()
return Context(this, message, candidate?.first, candidate?.second)
}
@ -91,14 +92,16 @@ abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) {
Logger.i { "Online! ${client.getSelf().username}" }
}
fun String.hasPrefix(): Pair<String, String>? {
private fun String.parsePrefixCommandAndArguments(): Pair<String, List<String>>? {
if (this.isBlank()) return null
var ret: Pair<String, String>? = null
var ret: Pair<String, List<String>>? = null
prefixes.forEach {
if (this.substring(0, it.length) == it) {
ret = Pair(this.substring(0, it.length), this.substring(it.length).split(" ").first())
val prefix = this.substring(0, it.length)
val cleanPrompt = this.substring(it.length)
ret = Pair(prefix, cleanPrompt.parseCommandAndArguments())
return@forEach
}
}

View file

@ -1,15 +1,14 @@
package io.github.null2264.tsukumogami.core
import io.github.null2264.tsukumogami.core.module.internal.Modules
import io.github.null2264.tsukumogami.core.module.BotModule
import io.github.null2264.tsukumogami.core.module.CommandHolder
import kotlin.reflect.KClass
import io.github.null2264.tsukumogami.core.commands.CommandHolder
import kotlin.reflect.KFunction
import kotlin.reflect.full.isSubclassOf
class BotConfigurator internal constructor() {
internal val commands = mutableMapOf<String, CommandHolder>()
internal val extensions = mutableListOf<KFunction<BotModule>>()
internal val extensions = Modules()
internal val prefixes = mutableListOf<String>()
var token: String = ""
@ -20,13 +19,7 @@ class BotConfigurator internal constructor() {
}
fun extensions(vararg extensions: KFunction<BotModule>) {
extensions.forEach {
val kClass = it.returnType.classifier as KClass<*>
if (!kClass.isSubclassOf(BotModule::class))
return
this.extensions.add(it)
}
this.extensions.initializeAndAddAll(extensions.toList())
}
fun prefixes(vararg prefixes: String) {

View file

@ -5,10 +5,10 @@ import dev.kord.core.entity.Message
import dev.kord.rest.builder.message.AllowedMentionsBuilder
import dev.kord.rest.builder.message.allowedMentions
class Context(private val bot: AbstractBot, private val message: Message, val prefix: String?, private val commandName: String?) {
class Context(private val bot: AbstractBot, private val message: Message, val prefix: String?, private val commandAndArguments: List<String>?) {
val author get() = message.author
val command get() = commandName?.let { bot.getCommand(it) }
val command get() = commandAndArguments?.get(0)?.let { bot.getCommand(it) }
suspend fun send(content: String) = message.channel.createMessage(content)

View file

@ -0,0 +1,12 @@
package io.github.null2264.tsukumogami.core.commands
import io.github.null2264.tsukumogami.core.commands.converters.Converter
data class Argument<T : Any?>(
val name: String,
val converter: Converter<T>,
) {
init {
converter.argumentObj = this
}
}

View file

@ -0,0 +1,16 @@
package io.github.null2264.tsukumogami.core.commands
import io.github.null2264.tsukumogami.core.commands.converters.Converter
open class Arguments {
val args = mutableListOf<Argument<*>>()
fun <R : Any> args(
name: String,
converter: Converter<R>
): Converter<R> {
args.add(Argument(name, converter))
return converter
}
}

View file

@ -1,11 +1,11 @@
package io.github.null2264.tsukumogami.core.module
package io.github.null2264.tsukumogami.core.commands
import kotlin.reflect.KFunction
/**
* Class holding information about a command
*/
internal data class CommandHolder(
data class CommandHolder internal constructor(
val name: String,
val extension: String,
val description: String? = null,

View file

@ -1,5 +1,4 @@
package io.github.null2264.tsukumogami.core.module.annotation
package io.github.null2264.tsukumogami.core.commands.annotation
/**
* Annotation to tag a function as command

View file

@ -0,0 +1,17 @@
package io.github.null2264.tsukumogami.core.commands.converters
import io.github.null2264.tsukumogami.core.commands.Argument
import io.github.null2264.tsukumogami.core.commands.Arguments
import kotlin.reflect.KProperty
abstract class Converter<OutputType: Any?> {
lateinit var argumentObj: Argument<OutputType>
abstract var parsed: OutputType
abstract suspend fun parse(input: String): OutputType
operator fun getValue(thisRef: Arguments, property: KProperty<*>): OutputType {
return parsed
}
}

View file

@ -0,0 +1,11 @@
package io.github.null2264.tsukumogami.core.commands.converters.impl
import io.github.null2264.tsukumogami.core.commands.converters.Converter
class StringConverter : Converter<String>() {
override var parsed: String = ""
override suspend fun parse(input: String): String {
this.parsed = input
return input
}
}

View file

@ -0,0 +1,6 @@
package io.github.null2264.tsukumogami.core.commands.ext
import io.github.null2264.tsukumogami.core.commands.Arguments
import io.github.null2264.tsukumogami.core.commands.converters.impl.StringConverter
fun Arguments.string(name: String) = args(name, StringConverter())

View file

@ -2,8 +2,9 @@ package io.github.null2264.tsukumogami.core.module
import co.touchlab.kermit.Logger
import io.github.null2264.tsukumogami.core.AbstractBot
import io.github.null2264.tsukumogami.core.module.annotation.Command
import io.github.null2264.tsukumogami.core.commands.annotation.Command
import io.github.null2264.tsukumogami.core.BotConfigurator
import io.github.null2264.tsukumogami.core.commands.CommandHolder
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.kotlinFunction

View file

@ -0,0 +1,40 @@
package io.github.null2264.tsukumogami.core.module.internal
import io.github.null2264.tsukumogami.core.module.BotModule
import java.util.function.Consumer
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.isSubclassOf
class Modules {
private val list = mutableListOf<BotModule>()
fun get(index: Int) = list[index]
private fun KFunction<BotModule>.tryInitialize(): BotModule? {
val kClass = this.returnType.classifier as KClass<*>
if (!kClass.isSubclassOf(BotModule::class))
return null
return this.call()
}
fun initializeAndAddAll(modules: List<KFunction<BotModule>>) {
addAll(modules.mapNotNull { it.tryInitialize() })
}
fun addAll(modules: List<BotModule>) {
list.addAll(modules)
}
fun add(module: KFunction<BotModule>) {
module.tryInitialize()?.let { add(it) }
}
fun add(module: BotModule) {
list.add(module)
}
fun forEach(consumer: Consumer<in BotModule>) = list.forEach(consumer)
}

View file

@ -0,0 +1,56 @@
package io.github.null2264.tsukumogami.core.utils
fun String.parseCommandAndArguments(): List<String> {
if (isBlank()) {
return emptyList()
}
val result = mutableListOf<String>()
val currentToken = StringBuilder()
var inQuotes = false
for (char in this) {
when (char) {
'"' -> {
if (inQuotes) {
// Closing quote: add the accumulated token
result.add(currentToken.toString())
currentToken.clear()
inQuotes = false
} else {
// Opening quote:
// If there's an existing token (e.g., word"another"), add it first
if (currentToken.isNotEmpty()) {
result.add(currentToken.toString())
currentToken.clear()
}
inQuotes = true
}
}
' ' -> {
if (inQuotes) {
// Space inside quotes: append it
currentToken.append(char)
} else {
// Space outside quotes: separator
if (currentToken.isNotEmpty()) {
result.add(currentToken.toString())
currentToken.clear()
}
// Ignore multiple spaces between tokens
}
}
else -> {
// Any other character: append it
currentToken.append(char)
}
}
}
// Add any remaining token after the loop (e.g., if the string doesn't end with a quote or space)
if (currentToken.isNotEmpty()) {
result.add(currentToken.toString())
}
return result
}