|
| 1 | +package org.yttr.koncorda.command |
| 2 | + |
| 3 | +import net.dv8tion.jda.api.events.message.MessageReceivedEvent |
| 4 | +import org.yttr.koncorda.CreationDsl |
| 5 | +import org.yttr.koncorda.command.check.CommandCheck |
| 6 | + |
| 7 | +/** |
| 8 | + * A callable bot command. |
| 9 | + */ |
| 10 | +sealed class Command( |
| 11 | + protected val depth: Int, |
| 12 | + private val prefix: String = "" |
| 13 | +) { |
| 14 | + protected val checks: MutableSet<CommandCheck> = mutableSetOf() |
| 15 | + protected val nextDepth = depth + 1 |
| 16 | + |
| 17 | + /** |
| 18 | + * Represents a command branch. |
| 19 | + * @param handler action to perform when branch is called a terminal, defaults to autogenerated help |
| 20 | + * @param prefix the command prefix, only used by the base route! |
| 21 | + */ |
| 22 | + class Branch(depth: Int = 0, handler: CommandHandler? = null, prefix: String = "") : Command(depth, prefix) { |
| 23 | + val routes = mutableMapOf<String, Command>() |
| 24 | + private val fallback by lazy { |
| 25 | + if (handler != null) Leaf(depth, handler) else { |
| 26 | + val autoHelpCommandHandler = object : CommandHandler { |
| 27 | + override fun CommandCall.handle() { |
| 28 | + val path = args.joinToString(" ") |
| 29 | + val validSubcommands = routes.keys.joinToString { "`$it`" } |
| 30 | + event.respond("Valid subcommands for `$path` are $validSubcommands.") |
| 31 | + } |
| 32 | + } |
| 33 | + Leaf(depth, autoHelpCommandHandler, true).also { it.checks.addAll(checks) } |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + /** |
| 38 | + * Try to follow a path to a terminal. |
| 39 | + * Cannot tailrec optimize: https://stackoverflow.com/a/44626117 |
| 40 | + */ |
| 41 | + operator fun get(path: List<String>): Leaf { |
| 42 | + val arg = path.firstOrNull() ?: return fallback |
| 43 | + |
| 44 | + return when (val foundCommand = routes[arg]) { |
| 45 | + is Branch -> foundCommand[path.drop(1)] |
| 46 | + is Leaf -> foundCommand |
| 47 | + else -> fallback |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + override fun addCheck(addedCheck: CommandCheck) { |
| 52 | + this.checks += addedCheck |
| 53 | + this.routes.values.forEach { it.addCheck(addedCheck) } |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Represents the end of a branch where a command will be handled. |
| 59 | + */ |
| 60 | + class Leaf( |
| 61 | + depth: Int, |
| 62 | + private val handler: CommandHandler, |
| 63 | + private val help: Boolean = false |
| 64 | + ) : Command(depth) { |
| 65 | + operator fun invoke(event: MessageReceivedEvent, args: List<String>) = with(handler) { |
| 66 | + CommandCall(event, if (help) args.take(depth) else args.drop(depth)).handle() |
| 67 | + } |
| 68 | + |
| 69 | + override fun addCheck(addedCheck: CommandCheck) { |
| 70 | + this.checks += addedCheck |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Build a branch. |
| 76 | + */ |
| 77 | + @CreationDsl |
| 78 | + fun Branch.branch(arg: String, handler: CommandHandler? = null, build: Branch.() -> Unit) { |
| 79 | + routes["$prefix$arg"] = Branch(nextDepth, handler).apply(build) |
| 80 | + } |
| 81 | + |
| 82 | + /** |
| 83 | + * Set the handler. |
| 84 | + */ |
| 85 | + @CreationDsl |
| 86 | + fun Branch.leaf(arg: String, handler: CommandHandler) { |
| 87 | + routes["$prefix$arg"] = Leaf(nextDepth, handler) |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * Set the handler. |
| 92 | + */ |
| 93 | + @CreationDsl |
| 94 | + fun Branch.leaf(arg: String, handler: CommandCall.() -> Unit) { |
| 95 | + routes["$prefix$arg"] = Leaf(nextDepth, object : CommandHandler { |
| 96 | + override fun CommandCall.handle() = handler() |
| 97 | + }) |
| 98 | + } |
| 99 | + |
| 100 | + /** |
| 101 | + * Create a checked branch. |
| 102 | + */ |
| 103 | + fun Branch.check(check: CommandCheck, build: Branch.() -> Unit) { |
| 104 | + val checkedBranch = Branch().also(build) |
| 105 | + checkedBranch.addCheck(check) |
| 106 | + this.routes += checkedBranch.routes |
| 107 | + } |
| 108 | + |
| 109 | + abstract fun addCheck(addedCheck: CommandCheck) |
| 110 | + |
| 111 | + fun check(event: MessageReceivedEvent) = checks.all { it.check(event) } |
| 112 | +} |
0 commit comments