From 04472f8bfa9637d5867fee8aec312074053b5e38 Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Fri, 23 May 2025 06:19:02 +0700 Subject: [PATCH] feat: Improve arguments * Add UserConverter * Add a way to specify default value --- .../bot/core/module/GeneralModule.kt | 3 +- .../core/module/arguments/TestArguments.kt | 9 ++- .../github/null2264/tsukumogami/core/Bot.kt | 2 +- .../null2264/tsukumogami/core/Context.kt | 12 +++- .../tsukumogami/core/commands/Arguments.kt | 13 ++--- .../tsukumogami/core/commands/Command.kt | 2 +- .../tsukumogami/core/commands/Group.kt | 6 +- .../core/commands/converters/Converter.kt | 10 +++- .../converters/impl/StringConverter.kt | 3 +- .../commands/converters/impl/UserConverter.kt | 57 +++++++++++++++++++ .../core/commands/ext/ArgumentsExtensions.kt | 15 ++++- .../tsukumogami/core/ext/KordExtensions.kt | 6 ++ .../tsukumogami/core/module/BotModule.kt | 2 +- 13 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/UserConverter.kt create mode 100644 core/src/main/kotlin/io/github/null2264/tsukumogami/core/ext/KordExtensions.kt diff --git a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt index f96494d..0c3bd10 100644 --- a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt +++ b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/GeneralModule.kt @@ -1,5 +1,6 @@ package io.github.null2264.tsukumogami.bot.core.module +import io.github.null2264.tsukumogami.bot.core.module.arguments.Test2Arguments import io.github.null2264.tsukumogami.bot.core.module.arguments.TestArguments import io.github.null2264.tsukumogami.core.module.api.botModules import kotlinx.datetime.Clock @@ -13,7 +14,7 @@ val generalModule = botModules("General") { } groups("group") { - commands("test", arguments = ::TestArguments) { ctx, args -> ctx.send("Hello world ${args.test}") } + commands("test", arguments = ::Test2Arguments) { ctx, args -> ctx.send("Hello world ${args.user}") } } commands("test", arguments = ::TestArguments) { ctx, args -> ctx.send("Hello world ${args.test}") } diff --git a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt index e252549..abc529f 100644 --- a/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt +++ b/bot/src/main/kotlin/io/github/null2264/tsukumogami/bot/core/module/arguments/TestArguments.kt @@ -2,7 +2,14 @@ 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 +import io.github.null2264.tsukumogami.core.commands.ext.user class TestArguments : Arguments() { - val test by string("Test") + val test by string("Test") { + default("Lmao") + } +} + +class Test2Arguments : Arguments() { + val user by user("User") } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt index b0283cc..418f46e 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Bot.kt @@ -69,7 +69,7 @@ open class Bot internal constructor(): IGroup { private fun getContext(message: Message): Context { val candidate = message.content.parsePrefixCommandAndArguments() val context = Context(this, message, candidate?.first, candidate?.second?.toMutableList()) - context.command = getCommand(context.candidate?.removeAt(0)) + context.command = getCommand(context.pullCandidateOrNull()) return context } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt index dc849eb..7c8cac4 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/Context.kt @@ -7,7 +7,7 @@ import dev.kord.rest.builder.message.allowedMentions import io.github.null2264.tsukumogami.core.commands.Command class Context( - private val bot: Bot, + val bot: Bot, private val message: Message, /** * The prefix that used to invoke the command @@ -19,6 +19,9 @@ class Context( val candidate: MutableList?, ) { + /** + * The user that invoked the command + */ val author get() = message.author /** @@ -26,6 +29,13 @@ class Context( */ var command: Command? = null + fun getCandidateOrNull() = candidate?.getOrNull(0) + fun pullCandidateOrNull() = candidate?.removeFirstOrNull() + fun pullCandidateIf(predicate: () -> Boolean): String? { + if (predicate()) return candidate?.removeFirstOrNull() + return null + } + suspend fun send(content: String) = message.channel.createMessage(content) suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage { diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt index e7adb40..d593c69 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Arguments.kt @@ -1,5 +1,6 @@ package io.github.null2264.tsukumogami.core.commands +import io.github.null2264.tsukumogami.core.Context import io.github.null2264.tsukumogami.core.commands.converters.Converter abstract class Arguments { @@ -15,17 +16,13 @@ abstract class Arguments { return converter } - suspend fun parse(values: List?) { - val currentValues = values?.toMutableList() + suspend fun parse(context: Context) { + val currentValues = context.candidate?.toMutableList() run { args.forEach { arg -> - val value = try { - currentValues?.removeAt(0) - } catch (e: Exception) { - null - } ?: return@run - arg.converter.parse(value) + val value = currentValues?.removeFirstOrNull() ?: return@run + arg.converter.parse(context, value) } } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Command.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Command.kt index 8dda97c..382e66a 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Command.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Command.kt @@ -13,7 +13,7 @@ open class Command( open suspend fun invoke(context: Context) { val parsedArguments = arguments.call() - parsedArguments.parse(context.candidate) + parsedArguments.parse(context) handler.invoke(context, parsedArguments) } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt index 8ce1afe..af7897d 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/Group.kt @@ -19,9 +19,9 @@ class Group( override val allCommands: MutableMap = mutableMapOf() override suspend fun invoke(context: Context) { - val command = allCommands[context.candidate?.get(0)] ?: return - context.candidate?.removeAt(0) - context.command = command + val command = allCommands[context.getCandidateOrNull()] + context.pullCandidateIf { command != null } ?: return + context.command = command!! command.invoke(context) } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt index aa0fe6a..2824df3 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/Converter.kt @@ -1,5 +1,6 @@ package io.github.null2264.tsukumogami.core.commands.converters +import io.github.null2264.tsukumogami.core.Context import io.github.null2264.tsukumogami.core.commands.Argument import io.github.null2264.tsukumogami.core.commands.Arguments import kotlin.reflect.KProperty @@ -10,9 +11,14 @@ abstract class Converter { abstract var parsed: OutputType - abstract suspend fun parse(input: String): OutputType + abstract suspend fun parse(context: Context, input: String): OutputType operator fun getValue(thisRef: Arguments, property: KProperty<*>): OutputType { - return parsed + return this.parsed + } + + fun default(defaultValue: OutputType): OutputType { + this.parsed = defaultValue + return this.parsed } } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt index 354817c..2a8a40d 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/StringConverter.kt @@ -1,12 +1,13 @@ package io.github.null2264.tsukumogami.core.commands.converters.impl +import io.github.null2264.tsukumogami.core.Context import io.github.null2264.tsukumogami.core.commands.converters.Converter class StringConverter : Converter() { override var parsed: String = "" - override suspend fun parse(input: String): String { + override suspend fun parse(context: Context, input: String): String { this.parsed = input return input } diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/UserConverter.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/UserConverter.kt new file mode 100644 index 0000000..b88d27e --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/converters/impl/UserConverter.kt @@ -0,0 +1,57 @@ +package io.github.null2264.tsukumogami.core.commands.converters.impl + +import dev.kord.common.entity.Snowflake +import dev.kord.core.entity.User +import io.github.null2264.tsukumogami.core.Context +import io.github.null2264.tsukumogami.core.annotation.TsukumogamiInternalApi +import io.github.null2264.tsukumogami.core.commands.converters.Converter +import io.github.null2264.tsukumogami.core.exceptions.CommandException +import io.github.null2264.tsukumogami.core.ext.users +import kotlinx.coroutines.flow.firstOrNull + +class UserConverter : Converter() { + + override lateinit var parsed: User + + override suspend fun parse(context: Context, input: String): User { + if (input.equals("me", true)) { + val user = context.author + if (user != null) { + this.parsed = user + return this.parsed + } + } + if (input.equals("you", true)) { + this.parsed = context.bot.client.getSelf() + return this.parsed + } + + this.parsed = context.findUser(input) ?: + throw CommandException("User not found") + + return this.parsed + } + + private suspend fun Context.findUser(arg: String): User? = + if (arg.startsWith("<@") && arg.endsWith(">")) { + val id: String = arg.substring(2, arg.length - 1).replace("!", "") + + try { + bot.client.getUser(Snowflake(id)) + } catch (_: NumberFormatException) { + throw CommandException("Invalid user ID") + } + } else { + try { + bot.client.getUser(Snowflake(arg)) + } catch (_: NumberFormatException) { + if (!arg.contains("#")) { + null + } else { + bot.client.users.firstOrNull { user -> + user.tag.equals(arg, true) + } + } + } + } +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt index c48824b..c603b45 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/commands/ext/ArgumentsExtensions.kt @@ -1,6 +1,19 @@ package io.github.null2264.tsukumogami.core.commands.ext +import dev.kord.core.entity.User import io.github.null2264.tsukumogami.core.commands.Arguments +import io.github.null2264.tsukumogami.core.commands.converters.Converter import io.github.null2264.tsukumogami.core.commands.converters.impl.StringConverter +import io.github.null2264.tsukumogami.core.commands.converters.impl.UserConverter -fun Arguments.string(name: String) = args(name, StringConverter()) +fun Arguments.string(name: String, declaration: Converter.() -> Unit = {}): Converter { + val converter = StringConverter() + declaration(converter) + return args(name, converter) +} + +fun Arguments.user(name: String, declaration: Converter.() -> Unit = {}): Converter { + val converter = UserConverter() + declaration(converter) + return args(name, converter) +} diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/ext/KordExtensions.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/ext/KordExtensions.kt new file mode 100644 index 0000000..728b106 --- /dev/null +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/ext/KordExtensions.kt @@ -0,0 +1,6 @@ +package io.github.null2264.tsukumogami.core.ext + +import dev.kord.core.Kord +import dev.kord.core.supplier.EntitySupplyStrategy + +val Kord.users get() = with(EntitySupplyStrategy.cache).users diff --git a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt index 2605e3a..de5571c 100644 --- a/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt +++ b/core/src/main/kotlin/io/github/null2264/tsukumogami/core/module/BotModule.kt @@ -4,7 +4,7 @@ import io.github.null2264.tsukumogami.core.Bot import io.github.null2264.tsukumogami.core.commands.Command import io.github.null2264.tsukumogami.core.commands.IGroup -class BotModule internal constructor(val name: String) : IGroup { +open class BotModule constructor(val name: String) : IGroup { override val allCommands: MutableMap = mutableMapOf()