refactor: Adjust how command is invoked

This commit is contained in:
Ahmad Ansori Palembani 2025-05-23 15:07:09 +07:00
parent 04472f8bfa
commit a62d86563a
6 changed files with 57 additions and 35 deletions

View file

@ -6,7 +6,7 @@ import io.github.null2264.tsukumogami.core.module.api.botModules
import kotlinx.datetime.Clock
val generalModule = botModules("General") {
commands("ping", description = "Ping the bot!") { ctx ->
commands("ping", alias = setOf("p"), description = "Ping the bot!") { ctx ->
val startTime = Clock.System.now()
ctx.typing()
val endTime = Clock.System.now()

View file

@ -34,18 +34,28 @@ open class Bot internal constructor(): IGroup {
_prefixes.add(prefix)
}
fun addCommand(command: Command) {
if (allCommands.containsKey(command.name)) {
private fun addCommand(name: String, command: Command) {
if (allCommands.containsKey(name)) {
throw IllegalStateException("Duplicate command: '${command.name}'")
}
allCommands[command.name] = command
}
fun removeCommand(command: Command) {
if (!allCommands.containsKey(command.name)) {
throw IllegalStateException("Command not found: '${command.name}'")
fun addCommand(command: Command) {
addCommand(command.name, command)
command.alias.forEach { addCommand(it, command) }
}
private fun removeCommand(name: String) {
if (!allCommands.containsKey(name)) {
throw IllegalStateException("Command not found: '${name}'")
}
allCommands.remove(command.name)
allCommands.remove(name)
}
fun removeCommand(command: Command) {
removeCommand(command.name)
command.alias.forEach { removeCommand(it) }
}
open suspend fun start() {
@ -67,9 +77,10 @@ open class Bot internal constructor(): IGroup {
fun getCommand(name: String?) = allCommands[name]
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.pullCandidateOrNull())
val parsed = message.content.parsePrefixAndCommand()
val context = Context(this, message, parsed?.first)
context.command = getCommand(parsed?.second)
if (context.command != null) context.invokedWith = parsed?.second
return context
}
@ -99,17 +110,18 @@ open class Bot internal constructor(): IGroup {
Logger.i { "Online! ${client.getSelf().username}" }
}
private fun String.parsePrefixCommandAndArguments(): Pair<String, List<String>>? {
private fun String.parsePrefixAndCommand(): Pair<String, String>? {
if (this.isBlank()) return null
var ret: Pair<String, List<String>>? = null
var ret: Pair<String, String>? = null
prefixes.forEach {
if (this.substring(0, it.length) == it) {
val prefix = this.substring(0, it.length)
val cleanPrompt = this.substring(it.length)
ret = Pair(prefix, cleanPrompt.parseCommandAndArguments())
return@forEach
run {
prefixes.forEach { candidate ->
val prefix = this.substring(0, candidate.length)
if (prefix != candidate) { return@forEach }
val command = this.drop(candidate.length).substringBefore(' ')
ret = prefix to command
return@run
}
}

View file

@ -8,34 +8,30 @@ import io.github.null2264.tsukumogami.core.commands.Command
class Context(
val bot: Bot,
private val message: Message,
val message: Message,
/**
* The prefix that used to invoke the command
*/
val prefix: String?,
/**
* Potential command name and/or arguments
*/
val candidate: MutableList<String>?,
) {
/**
* The user that invoked the command
*
* Alias to [Message.author]
*/
val author get() = message.author
/**
* The text that invoked the command
*/
var invokedWith: String? = null
/**
* The command that currently being invoked
*/
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 {
@ -48,4 +44,15 @@ class Context(
}
suspend fun typing() = message.channel.type()
fun parseArguments(): MutableList<String>? =
if (prefix != null && invokedWith != null) {
message.content
.substringAfter("$prefix$invokedWith")
.trim()
.split(' ')
.toMutableList()
} else {
null
}
}

View file

@ -17,7 +17,7 @@ abstract class Arguments {
}
suspend fun parse(context: Context) {
val currentValues = context.candidate?.toMutableList()
val currentValues = context.parseArguments()
run {
args.forEach { arg ->

View file

@ -19,9 +19,13 @@ class Group(
override val allCommands: MutableMap<String, Command> = mutableMapOf()
override suspend fun invoke(context: Context) {
val command = allCommands[context.getCandidateOrNull()]
context.pullCandidateIf { command != null } ?: return
context.command = command!!
val subcommandName = context.message.content
.substringAfter("${context.prefix}${context.invokedWith}")
.trim()
.substringBefore(' ')
val command = allCommands[subcommandName] ?: return
context.invokedWith += " $subcommandName"
context.command = command
command.invoke(context)
}
}

View file

@ -9,7 +9,6 @@ interface IGroup {
private fun addCommand(command: Command) {
allCommands[command.name] = command
command.alias.forEach { allCommands[it] = command }
}
fun commands(