mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Command registering, matching, executing
This commit is contained in:
parent
41112affa8
commit
ee8be9799f
@ -40,6 +40,8 @@ dependencies {
|
||||
|
||||
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
|
||||
testApi(kotlin("stdlib"))
|
||||
testApi(kotlin("test"))
|
||||
testApi(kotlin("test-junit5"))
|
||||
}
|
||||
|
||||
version = Versions.Mirai.console
|
||||
|
@ -1,16 +0,0 @@
|
||||
package net.mamoe.mirai.console.command;
|
||||
|
||||
// import jdk.jfr.Description;
|
||||
|
||||
public class JCommandManager {
|
||||
|
||||
private JCommandManager() {
|
||||
|
||||
}
|
||||
|
||||
public static CommandManager getInstance() {
|
||||
return CommandManager.INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package net.mamoe.mirai.console.utils;
|
||||
|
||||
import net.mamoe.mirai.Bot;
|
||||
import net.mamoe.mirai.console.MiraiConsole;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,7 +11,7 @@ import java.util.List;
|
||||
public class BotManager {
|
||||
|
||||
public static List<Long> getManagers(long botAccount) {
|
||||
Bot bot = MiraiConsole.INSTANCE.getBotOrThrow(botAccount);
|
||||
Bot bot = Bot.getInstance(botAccount);
|
||||
return getManagers(bot);
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,10 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.io.charsets.Charset
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.plugins.PluginManager
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
@ -42,7 +43,7 @@ interface IMiraiConsole : CoroutineScope {
|
||||
val mainLogger: MiraiLogger
|
||||
}
|
||||
|
||||
object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||
object MiraiConsole : CoroutineScope, IMiraiConsole, CommandOwner {
|
||||
private lateinit var instance: IMiraiConsole
|
||||
|
||||
/** 由前端调用 */
|
||||
@ -58,18 +59,20 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
||||
|
||||
init {
|
||||
DefaultLogger = {
|
||||
this.newLogger(it)
|
||||
}
|
||||
this.coroutineContext[Job]!!.invokeOnCompletion {
|
||||
PluginManager.disablePlugins()
|
||||
CommandManager.cancel()
|
||||
Bot.botInstances.forEach {
|
||||
it.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
||||
}
|
||||
|
||||
|
||||
internal val Throwable.stacktraceString: String
|
||||
get() =
|
||||
ByteArrayOutputStream().apply {
|
||||
|
@ -7,15 +7,8 @@ import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.plugins.PluginManager
|
||||
import net.mamoe.mirai.console.utils.retryCatching
|
||||
import net.mamoe.mirai.console.utils.tryNTimes
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
internal object CuiPluginCenter : PluginCenter {
|
||||
|
||||
@ -96,6 +89,8 @@ internal object CuiPluginCenter : PluginCenter {
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File {
|
||||
TODO()
|
||||
/*
|
||||
val info = findPlugin(name) ?: error("Plugin Not Found")
|
||||
val targetFile = File(PluginManager.pluginsPath, "$name-" + info.version + ".jar")
|
||||
withContext(Dispatchers.IO) {
|
||||
@ -116,6 +111,7 @@ internal object CuiPluginCenter : PluginCenter {
|
||||
}
|
||||
}
|
||||
return targetFile
|
||||
*/
|
||||
}
|
||||
|
||||
override val name: String
|
||||
|
@ -13,129 +13,134 @@ package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.plugins.PluginBase
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
internal const val FOR_BINARY_COMPATIBILITY = "for binary compatibility"
|
||||
|
||||
/**
|
||||
* 指令
|
||||
*
|
||||
* @see register 注册这个指令
|
||||
* @see registerCommand 注册指令 DSL
|
||||
*/
|
||||
interface Command {
|
||||
/**
|
||||
* 指令主名称
|
||||
*/
|
||||
val name: String
|
||||
val owner: CommandOwner
|
||||
val descriptor: CommandDescriptor
|
||||
|
||||
/*
|
||||
@Deprecated(FOR_BINARY_COMPATIBILITY, level = DeprecationLevel.HIDDEN)
|
||||
suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
|
||||
return true
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 别名
|
||||
* 执行这个指令.
|
||||
*/
|
||||
val alias: List<String>
|
||||
|
||||
/**
|
||||
* 描述, 将会显示在 "/help" 指令中
|
||||
*/
|
||||
val description: String
|
||||
|
||||
/**
|
||||
* 用法说明
|
||||
*/
|
||||
val usage: String
|
||||
|
||||
suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean
|
||||
}
|
||||
|
||||
abstract class AbstractCommand(
|
||||
override val name: String,
|
||||
override val alias: List<String>,
|
||||
override val description: String,
|
||||
override val usage: String
|
||||
) : Command
|
||||
|
||||
/**
|
||||
* 注册这个指令
|
||||
*/
|
||||
inline fun Command.register(commandOwner: CommandOwner) = CommandManager.register(commandOwner, this)
|
||||
|
||||
internal inline fun registerConsoleCommands(builder: CommandBuilder.() -> Unit): Command {
|
||||
return CommandBuilder().apply(builder).register(ConsoleCommandOwner)
|
||||
suspend fun onCommand(sender: CommandSender, args: CommandArgs): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造并注册一个指令
|
||||
* 指令实际参数列表. 参数顺序与 [Command.descriptor] 的 [CommandDescriptor.params] 相同.
|
||||
*/
|
||||
inline fun PluginBase.registerCommand(builder: CommandBuilder.() -> Unit): Command {
|
||||
return CommandBuilder().apply(builder).register(this.asCommandOwner())
|
||||
}
|
||||
|
||||
|
||||
// for java
|
||||
@Suppress("unused")
|
||||
abstract class BlockingCommand(
|
||||
override val name: String,
|
||||
override val alias: List<String> = listOf(),
|
||||
override val description: String = "",
|
||||
override val usage: String = ""
|
||||
) : Command {
|
||||
class CommandArgs private constructor(
|
||||
@JvmField
|
||||
internal val values: List<Any>,
|
||||
private val fromCommand: Command
|
||||
) : List<Any> by values {
|
||||
/**
|
||||
* 最高优先级监听器.
|
||||
* 获取第一个类型为 [R] 的参数
|
||||
*/
|
||||
@JvmSynthetic
|
||||
inline fun <reified R> getReified(): R {
|
||||
for (value in this) {
|
||||
if (value is R) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
error("Cannot find argument typed ${R::class.qualifiedName}")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称为 [name] 的参数.
|
||||
*
|
||||
* 指令调用将优先触发 [Command.onCommand], 若该函数返回 `false`, 则不会调用 [PluginBase.onCommand]
|
||||
* */
|
||||
final override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
|
||||
return withContext(Dispatchers.IO) {
|
||||
onCommandBlocking(sender, args)
|
||||
* 若 [name] 为 `null` 则获取第一个匿名参数
|
||||
* @throws NoSuchElementException 找不到这个名称的参数时抛出
|
||||
*/
|
||||
operator fun get(name: String?): Any {
|
||||
val index = fromCommand.descriptor.params.indexOfFirst { it.name == name }
|
||||
if (index == -1) {
|
||||
throw NoSuchElementException("Cannot find argument named $name")
|
||||
}
|
||||
return values[index]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称为 [name] 的参数. 并强转为 [R].
|
||||
*
|
||||
* 若 [name] 为 `null` 则获取第一个匿名参数
|
||||
* @throws IllegalStateException 无法强转时抛出
|
||||
*/
|
||||
fun <R> getAs(name: String?): R {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return this[name] as? R ?: error("Argument $name has a type $")
|
||||
}
|
||||
|
||||
/** 获取第一个类型为 [R] 的参数并提供委托 */
|
||||
inline operator fun <reified R : Any> getValue(thisRef: Any?, property: KProperty<*>): R = getReified()
|
||||
|
||||
companion object {
|
||||
fun parseFrom(command: Command, sender: CommandSender, rawArgs: List<Any>): CommandArgs {
|
||||
val params = command.descriptor.params
|
||||
|
||||
require(rawArgs.size >= params.size) { "No enough rawArgs: required ${params.size}, found only ${rawArgs.size}" }
|
||||
|
||||
command.descriptor.params.asSequence().zip(rawArgs.asSequence()).map { (commandParam, any) ->
|
||||
command.parserFor(commandParam)?.parse(any, sender)
|
||||
?: error("ICould not find a parser for param named ${commandParam.name}")
|
||||
}.toList().let { bakedArgs ->
|
||||
return CommandArgs(bakedArgs, command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun onCommandBlocking(sender: CommandSender, args: List<String>): Boolean
|
||||
}
|
||||
|
||||
inline val Command.fullName get() = descriptor.fullName
|
||||
inline val Command.usage get() = descriptor.usage
|
||||
inline val Command.params get() = descriptor.params
|
||||
inline val Command.description get() = descriptor.description
|
||||
inline val Command.context get() = descriptor.context
|
||||
inline val Command.aliases get() = descriptor.aliases
|
||||
inline val Command.permission get() = descriptor.permission
|
||||
inline val Command.allNames get() = descriptor.allNames
|
||||
|
||||
abstract class PluginCommand(
|
||||
final override val owner: PluginBase,
|
||||
descriptor: CommandDescriptor
|
||||
) : AbstractCommand(descriptor)
|
||||
|
||||
internal abstract class ConsoleCommand(
|
||||
descriptor: CommandDescriptor
|
||||
) : AbstractCommand(descriptor) {
|
||||
final override val owner: MiraiConsole get() = MiraiConsole
|
||||
}
|
||||
|
||||
sealed class AbstractCommand(
|
||||
final override val descriptor: CommandDescriptor
|
||||
) : Command
|
||||
|
||||
|
||||
/**
|
||||
* @see registerCommand
|
||||
* For Java
|
||||
*/
|
||||
class CommandBuilder @PublishedApi internal constructor() {
|
||||
var name: String? = null
|
||||
var alias: List<String>? = null
|
||||
var description: String = ""
|
||||
var usage: String = "use /help for help"
|
||||
|
||||
internal var onCommand: (suspend CommandSender.(args: List<String>) -> Boolean)? = null
|
||||
|
||||
fun onCommand(commandProcess: suspend CommandSender.(args: List<String>) -> Boolean) {
|
||||
onCommand = commandProcess
|
||||
@Suppress("unused")
|
||||
abstract class BlockingCommand(
|
||||
owner: PluginBase,
|
||||
descriptor: CommandDescriptor
|
||||
) : PluginCommand(owner, descriptor) {
|
||||
final override suspend fun onCommand(sender: CommandSender, args: CommandArgs): Boolean {
|
||||
return withContext(Dispatchers.IO) { onCommandBlocking(sender, args) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal
|
||||
|
||||
|
||||
internal class AnonymousCommand internal constructor(
|
||||
override val name: String,
|
||||
override val alias: List<String>,
|
||||
override val description: String,
|
||||
override val usage: String = "",
|
||||
val onCommand: suspend CommandSender.(args: List<String>) -> Boolean
|
||||
) : Command {
|
||||
override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
|
||||
return onCommand.invoke(sender, args)
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun CommandBuilder.register(commandOwner: CommandOwner): AnonymousCommand {
|
||||
if (name == null || onCommand == null) {
|
||||
error("CommandBuilder not complete")
|
||||
}
|
||||
if (alias == null) {
|
||||
alias = listOf()
|
||||
}
|
||||
return AnonymousCommand(
|
||||
name!!,
|
||||
alias!!,
|
||||
description,
|
||||
usage,
|
||||
onCommand!!
|
||||
).also { it.register(commandOwner) }
|
||||
abstract fun onCommandBlocking(sender: CommandSender, args: CommandArgs): Boolean
|
||||
}
|
@ -17,8 +17,20 @@ import kotlin.contracts.contract
|
||||
* input is always String
|
||||
*/
|
||||
abstract class CommandArgParser<out T : Any> {
|
||||
abstract fun parse(s: String, sender: CommandSender): T
|
||||
open fun parse(s: SingleMessage, sender: CommandSender): T = parse(s.content, sender)
|
||||
abstract fun parse(raw: String, sender: CommandSender): T
|
||||
open fun parse(raw: SingleMessage, sender: CommandSender): T = parse(raw.content, sender)
|
||||
}
|
||||
|
||||
fun <T : Any> CommandArgParser<T>.parse(raw: Any, sender: CommandSender): T {
|
||||
contract {
|
||||
returns() implies (raw is String || raw is SingleMessage)
|
||||
}
|
||||
|
||||
return when (raw) {
|
||||
is String -> parse(raw, sender)
|
||||
is SingleMessage -> parse(raw, sender)
|
||||
else -> throw IllegalArgumentException("Illegal raw argument type: ${raw::class.qualifiedName}")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@ -46,7 +58,7 @@ inline fun CommandArgParser<*>.checkArgument(
|
||||
inline fun <T : Any> CommandArgParser(
|
||||
crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
|
||||
): CommandArgParser<T> = object : CommandArgParser<T>() {
|
||||
override fun parse(s: String, sender: CommandSender): T = parser(s, sender)
|
||||
override fun parse(raw: String, sender: CommandSender): T = parser(raw, sender)
|
||||
}
|
||||
|
||||
|
||||
@ -57,41 +69,41 @@ class ParserException(message: String, cause: Throwable? = null) : RuntimeExcept
|
||||
|
||||
|
||||
object IntArgParser : CommandArgParser<Int>() {
|
||||
override fun parse(s: String, sender: CommandSender): Int =
|
||||
s.toIntOrNull() ?: illegalArgument("无法解析 $s 为整数")
|
||||
override fun parse(raw: String, sender: CommandSender): Int =
|
||||
raw.toIntOrNull() ?: illegalArgument("无法解析 $raw 为整数")
|
||||
}
|
||||
|
||||
object LongArgParser : CommandArgParser<Long>() {
|
||||
override fun parse(s: String, sender: CommandSender): Long =
|
||||
s.toLongOrNull() ?: illegalArgument("无法解析 $s 为长整数")
|
||||
override fun parse(raw: String, sender: CommandSender): Long =
|
||||
raw.toLongOrNull() ?: illegalArgument("无法解析 $raw 为长整数")
|
||||
}
|
||||
|
||||
object ShortArgParser : CommandArgParser<Short>() {
|
||||
override fun parse(s: String, sender: CommandSender): Short =
|
||||
s.toShortOrNull() ?: illegalArgument("无法解析 $s 为短整数")
|
||||
override fun parse(raw: String, sender: CommandSender): Short =
|
||||
raw.toShortOrNull() ?: illegalArgument("无法解析 $raw 为短整数")
|
||||
}
|
||||
|
||||
object ByteArgParser : CommandArgParser<Byte>() {
|
||||
override fun parse(s: String, sender: CommandSender): Byte =
|
||||
s.toByteOrNull() ?: illegalArgument("无法解析 $s 为字节")
|
||||
override fun parse(raw: String, sender: CommandSender): Byte =
|
||||
raw.toByteOrNull() ?: illegalArgument("无法解析 $raw 为字节")
|
||||
}
|
||||
|
||||
object DoubleArgParser : CommandArgParser<Double>() {
|
||||
override fun parse(s: String, sender: CommandSender): Double =
|
||||
s.toDoubleOrNull() ?: illegalArgument("无法解析 $s 为小数")
|
||||
override fun parse(raw: String, sender: CommandSender): Double =
|
||||
raw.toDoubleOrNull() ?: illegalArgument("无法解析 $raw 为小数")
|
||||
}
|
||||
|
||||
object FloatArgParser : CommandArgParser<Float>() {
|
||||
override fun parse(s: String, sender: CommandSender): Float =
|
||||
s.toFloatOrNull() ?: illegalArgument("无法解析 $s 为小数")
|
||||
override fun parse(raw: String, sender: CommandSender): Float =
|
||||
raw.toFloatOrNull() ?: illegalArgument("无法解析 $raw 为小数")
|
||||
}
|
||||
|
||||
object StringArgParser : CommandArgParser<String>() {
|
||||
override fun parse(s: String, sender: CommandSender): String = s
|
||||
override fun parse(raw: String, sender: CommandSender): String = raw
|
||||
}
|
||||
|
||||
object BooleanArgParser : CommandArgParser<Boolean>() {
|
||||
override fun parse(s: String, sender: CommandSender): Boolean = s.trim().let { str ->
|
||||
override fun parse(raw: String, sender: CommandSender): Boolean = raw.trim().let { str ->
|
||||
str.equals("true", ignoreCase = true)
|
||||
|| str.equals("yes", ignoreCase = true)
|
||||
|| str.equals("enabled", ignoreCase = true)
|
||||
@ -105,11 +117,11 @@ object BooleanArgParser : CommandArgParser<Boolean>() {
|
||||
* errors: String->Int convert, Bot Not Exist
|
||||
*/
|
||||
object ExistBotArgParser : CommandArgParser<Bot>() {
|
||||
override fun parse(s: String, sender: CommandSender): Bot {
|
||||
override fun parse(raw: String, sender: CommandSender): Bot {
|
||||
val uin = try {
|
||||
s.toLong()
|
||||
raw.toLong()
|
||||
} catch (e: Exception) {
|
||||
error("无法识别QQ UIN$s")
|
||||
error("无法识别QQ UIN$raw")
|
||||
}
|
||||
return try {
|
||||
Bot.getInstance(uin)
|
||||
@ -123,8 +135,8 @@ object ExistFriendArgParser : CommandArgParser<Friend>() {
|
||||
//Bot.friend
|
||||
//friend
|
||||
//~ = self
|
||||
override fun parse(s: String, sender: CommandSender): Friend {
|
||||
if (s == "~") {
|
||||
override fun parse(raw: String, sender: CommandSender): Friend {
|
||||
if (raw == "~") {
|
||||
if (sender !is BotAware) {
|
||||
illegalArgument("无法解析~作为默认")
|
||||
}
|
||||
@ -141,20 +153,20 @@ object ExistFriendArgParser : CommandArgParser<Friend>() {
|
||||
}
|
||||
if (sender is BotAware) {
|
||||
return try {
|
||||
sender.bot.friends[s.toLong()]
|
||||
sender.bot.friends[raw.toLong()]
|
||||
} catch (e: NoSuchElementException) {
|
||||
error("无法找到" + s + "这个好友")
|
||||
error("无法找到" + raw + "这个好友")
|
||||
} catch (e: NumberFormatException) {
|
||||
error("无法解析$s")
|
||||
error("无法解析$raw")
|
||||
}
|
||||
} else {
|
||||
s.split(".").let { args ->
|
||||
raw.split(".").let { args ->
|
||||
if (args.size != 2) {
|
||||
illegalArgument("无法解析 $s, 格式应为 机器人账号.好友账号")
|
||||
illegalArgument("无法解析 $raw, 格式应为 机器人账号.好友账号")
|
||||
}
|
||||
return try {
|
||||
Bot.getInstance(args[0].toLong()).friends.getOrNull(
|
||||
args[1].toLongOrNull() ?: illegalArgument("无法解析 $s 为好友")
|
||||
args[1].toLongOrNull() ?: illegalArgument("无法解析 $raw 为好友")
|
||||
) ?: illegalArgument("无法找到好友 ${args[1]}")
|
||||
} catch (e: NoSuchElementException) {
|
||||
illegalArgument("无法找到机器人账号 ${args[0]}")
|
||||
@ -163,28 +175,28 @@ object ExistFriendArgParser : CommandArgParser<Friend>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun parse(s: SingleMessage, sender: CommandSender): Friend {
|
||||
if (s is At) {
|
||||
override fun parse(raw: SingleMessage, sender: CommandSender): Friend {
|
||||
if (raw is At) {
|
||||
assert(sender is GroupContactCommandSender)
|
||||
return (sender as BotAware).bot.friends.getOrNull(s.target) ?: illegalArgument("At的对象非Bot好友")
|
||||
return (sender as BotAware).bot.friends.getOrNull(raw.target) ?: illegalArgument("At的对象非Bot好友")
|
||||
} else {
|
||||
error("无法解析 $s 为好友")
|
||||
error("无法解析 $raw 为好友")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ExistGroupArgParser : CommandArgParser<Group>() {
|
||||
override fun parse(s: String, sender: CommandSender): Group {
|
||||
override fun parse(raw: String, sender: CommandSender): Group {
|
||||
//by default
|
||||
if ((s == "" || s == "~") && sender is GroupContactCommandSender) {
|
||||
if ((raw == "" || raw == "~") && sender is GroupContactCommandSender) {
|
||||
return sender.contact as Group
|
||||
}
|
||||
//from bot to group
|
||||
if (sender is BotAware) {
|
||||
val code = try {
|
||||
s.toLong()
|
||||
raw.toLong()
|
||||
} catch (e: NoSuchElementException) {
|
||||
error("无法识别Group Code$s")
|
||||
error("无法识别Group Code$raw")
|
||||
}
|
||||
return try {
|
||||
sender.bot.getGroup(code)
|
||||
@ -193,7 +205,7 @@ object ExistGroupArgParser : CommandArgParser<Group>() {
|
||||
}
|
||||
}
|
||||
//from console/other
|
||||
return with(s.split(".")) {
|
||||
return with(raw.split(".")) {
|
||||
if (this.size != 2) {
|
||||
error("请使用BotQQ号.群号 来表示Bot的一个群")
|
||||
}
|
||||
@ -213,9 +225,9 @@ object ExistMemberArgParser : CommandArgParser<Member>() {
|
||||
//私聊: Group.Member[QQ/名片]
|
||||
//群内: Q号
|
||||
//群内: 名片
|
||||
override fun parse(s: String, sender: CommandSender): Member {
|
||||
override fun parse(raw: String, sender: CommandSender): Member {
|
||||
if (sender !is BotAware) {
|
||||
with(s.split(".")) {
|
||||
with(raw.split(".")) {
|
||||
checkArgument(this.size >= 3) {
|
||||
"无法识别Member, 请使用Bot.Group.Member[QQ/名片]的格式"
|
||||
}
|
||||
@ -246,12 +258,12 @@ object ExistMemberArgParser : CommandArgParser<Member>() {
|
||||
if (sender is GroupContactCommandSender) {
|
||||
val group = sender.contact as Group
|
||||
return try {
|
||||
group.members[s.toLong()]
|
||||
group.members[raw.toLong()]
|
||||
} catch (ignored: Exception) {
|
||||
group.fuzzySearchMember(s) ?: illegalArgument("无法找到成员$s")
|
||||
group.fuzzySearchMember(raw) ?: illegalArgument("无法找到成员$raw")
|
||||
}
|
||||
} else {
|
||||
with(s.split(".")) {
|
||||
with(raw.split(".")) {
|
||||
if (this.size < 2) {
|
||||
illegalArgument("无法识别Member, 请使用Group.Member[QQ/名片]的格式")
|
||||
}
|
||||
@ -274,12 +286,12 @@ object ExistMemberArgParser : CommandArgParser<Member>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun parse(s: SingleMessage, sender: CommandSender): Member {
|
||||
return if (s is At) {
|
||||
override fun parse(raw: SingleMessage, sender: CommandSender): Member {
|
||||
return if (raw is At) {
|
||||
checkArgument(sender is GroupContactCommandSender)
|
||||
(sender.contact as Group).members[s.target]
|
||||
(sender.contact as Group).members[raw.target]
|
||||
} else {
|
||||
illegalArgument("无法识别Member" + s.content)
|
||||
illegalArgument("无法识别Member" + raw.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@ -11,15 +15,19 @@ class CommandDescriptor(
|
||||
/**
|
||||
* 包含子命令的全名. 如 "`group kick`", 其中 `kick` 为 `group` 的子命令
|
||||
*/
|
||||
val fullName: String,
|
||||
fullName: CommandFullName,
|
||||
/**
|
||||
* 用法说明
|
||||
*/
|
||||
val usage: String,
|
||||
usage: String,
|
||||
/**
|
||||
* 指令参数列表, 有顺序.
|
||||
*/
|
||||
val params: List<CommandParam<*>>,
|
||||
/**
|
||||
* 指令说明
|
||||
*/
|
||||
description: String = "",
|
||||
/**
|
||||
* 指令参数解析器环境.
|
||||
*/
|
||||
@ -27,7 +35,7 @@ class CommandDescriptor(
|
||||
/**
|
||||
* 指令别名
|
||||
*/
|
||||
val aliases: Array<String> = arrayOf(),
|
||||
aliases: Array<CommandFullName> = arrayOf(),
|
||||
/**
|
||||
* 指令权限
|
||||
*
|
||||
@ -35,19 +43,101 @@ class CommandDescriptor(
|
||||
* @see CommandPermission.and 同时要求两个权限
|
||||
*/
|
||||
val permission: CommandPermission = CommandPermission.Default
|
||||
)
|
||||
) {
|
||||
/**
|
||||
* 指令别名
|
||||
*/
|
||||
val aliases: Array<CommandFullName> = aliases.map { it.checkFullName("alias") }.toTypedArray()
|
||||
|
||||
/**
|
||||
* 指令说明
|
||||
*/
|
||||
val description: String = description.trim()
|
||||
|
||||
/**
|
||||
* 用法说明
|
||||
*/
|
||||
val usage: String = usage.trim()
|
||||
|
||||
/**
|
||||
* 包含子命令的全名. 如 "`group kick`", 其中 `kick` 为 `group` 的子命令
|
||||
* 元素类型可以为 [Message] 或 [String]
|
||||
*/
|
||||
val fullName: CommandFullName = fullName.checkFullName("fullName")
|
||||
|
||||
/**
|
||||
* `fullName + aliases`
|
||||
*/
|
||||
val allNames = arrayOf(fullName, *aliases)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CommandDescriptor
|
||||
|
||||
if (!fullName.contentEquals(other.fullName)) return false
|
||||
if (usage != other.usage) return false
|
||||
if (params != other.params) return false
|
||||
if (description != other.description) return false
|
||||
if (context != other.context) return false
|
||||
if (!aliases.contentEquals(other.aliases)) return false
|
||||
if (permission != other.permission) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = fullName.hashCode()
|
||||
result = 31 * result + usage.hashCode()
|
||||
result = 31 * result + params.hashCode()
|
||||
result = 31 * result + description.hashCode()
|
||||
result = 31 * result + context.hashCode()
|
||||
result = 31 * result + aliases.contentHashCode()
|
||||
result = 31 * result + permission.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Command.checkArgs(args: CommandArgs) = this.descriptor.checkArgs(args)
|
||||
fun CommandDescriptor.checkArgs(args: CommandArgs) {
|
||||
require(args.size >= this.params.size) { "No enough args. Required ${params.size}, but given ${args.size}" }
|
||||
params.forEachIndexed { index, commandParam ->
|
||||
require(commandParam.type.isInstance(args[index])) {
|
||||
"Illegal arg #$index, required ${commandParam.type.qualifiedName}, but given ${args[index]::class.qualifiedName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Any.flattenCommandComponents(): Sequence<Any> = when (this) {
|
||||
is Array<*> -> this.asSequence().flatMap {
|
||||
it?.flattenCommandComponents() ?: throw java.lang.IllegalArgumentException("unexpected null value")
|
||||
}
|
||||
is String -> splitToSequence(' ').filterNot { it.isBlank() }
|
||||
is PlainText -> content.flattenCommandComponents()
|
||||
is SingleMessage -> sequenceOf(this)
|
||||
is MessageChain -> this.asSequence().map { it.flattenCommandComponents() }
|
||||
else -> throw IllegalArgumentException("Illegal component: $this")
|
||||
}
|
||||
|
||||
internal fun CommandFullName.checkFullName(errorHint: String): CommandFullName {
|
||||
return flattenCommandComponents().toList().also {
|
||||
require(it.isNotEmpty()) { "$errorHint must not be empty" }
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个 [CommandDescriptor]
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
inline fun CommandDescriptor(
|
||||
fullName: String,
|
||||
block: CommandDescriptorBuilder.() -> Unit
|
||||
vararg fullName: Any,
|
||||
block: CommandDescriptorBuilder.() -> Unit = {}
|
||||
): CommandDescriptor = CommandDescriptorBuilder(fullName).apply(block).build()
|
||||
|
||||
class CommandDescriptorBuilder(
|
||||
val fullName: String
|
||||
vararg val fullName: Any
|
||||
) {
|
||||
@PublishedApi
|
||||
internal var context: CommandParserContext = CommandParserContext.Builtins
|
||||
@ -62,7 +152,10 @@ class CommandDescriptorBuilder(
|
||||
internal var usage: String = "<no usage>"
|
||||
|
||||
@PublishedApi
|
||||
internal var aliases: MutableList<String> = mutableListOf()
|
||||
internal var aliases: MutableList<CommandFullName> = mutableListOf()
|
||||
|
||||
@PublishedApi
|
||||
internal var description: String = ""
|
||||
|
||||
/** 增加指令参数解析器列表 */
|
||||
@JvmSynthetic
|
||||
@ -91,8 +184,15 @@ class CommandDescriptorBuilder(
|
||||
usage = message
|
||||
}
|
||||
|
||||
fun alias(vararg name: String): CommandDescriptorBuilder = apply {
|
||||
this.aliases.addAll(name)
|
||||
fun description(description: String): CommandDescriptorBuilder = apply {
|
||||
this.description = description
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个别名
|
||||
*/
|
||||
fun alias(vararg fullName: Any): CommandDescriptorBuilder = apply {
|
||||
this.aliases.add(fullName)
|
||||
}
|
||||
|
||||
fun param(vararg params: CommandParam<*>): CommandDescriptorBuilder = apply {
|
||||
@ -105,7 +205,7 @@ class CommandDescriptorBuilder(
|
||||
type: KClass<T>,
|
||||
overrideParser: CommandArgParser<T>? = null
|
||||
): CommandDescriptorBuilder = apply {
|
||||
this.params.add(CommandParam(name, type).apply { this.parser = overrideParser })
|
||||
this.params.add(CommandParam(name, type).apply { this._overrideParser = overrideParser })
|
||||
}
|
||||
|
||||
fun <T : Any> param(
|
||||
@ -143,7 +243,7 @@ class CommandDescriptorBuilder(
|
||||
}
|
||||
|
||||
fun build(): CommandDescriptor =
|
||||
CommandDescriptor(fullName, context, params, usage, aliases.toTypedArray(), permission)
|
||||
CommandDescriptor(fullName, usage, params, description, context, aliases.toTypedArray(), permission)
|
||||
}
|
||||
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
@ -154,7 +254,7 @@ inline class ParamBlock internal constructor(@PublishedApi internal val list: Mu
|
||||
|
||||
/** 指定 [CommandParam.overrideParser] */
|
||||
infix fun <T : Any> CommandParam<T>.using(parser: CommandArgParser<T>): CommandParam<T> =
|
||||
this.apply { this.parser = parser }
|
||||
this.apply { this._overrideParser = parser }
|
||||
|
||||
/** 覆盖 [CommandArgParser] */
|
||||
inline infix fun <reified T : Any> String.using(parser: CommandArgParser<T>): CommandParam<T> =
|
||||
|
@ -1,3 +1,121 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
@file:JvmName("CommandManager")
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
object CommandManager
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
typealias CommandFullName = Array<out Any>
|
||||
|
||||
interface CommandOwner
|
||||
|
||||
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
|
||||
|
||||
fun CommandOwner.unregisterAllCommands() {
|
||||
for (registeredCommand in registeredCommands) {
|
||||
registeredCommand.unregister()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令. 若此指令已经注册或有已经注册的指令与 [allNames] 重名, 返回 `false`
|
||||
*/
|
||||
fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
||||
with(descriptor) {
|
||||
if (findDuplicate() != null) {
|
||||
return false
|
||||
}
|
||||
InternalCommandManager.registeredCommands.add(this@register)
|
||||
for (name in this.allNames) {
|
||||
InternalCommandManager.nameToCommandMap[name] = this@register
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找是否有重名的指令. 返回重名的指令.
|
||||
*/
|
||||
fun Command.findDuplicate(): Command? {
|
||||
return InternalCommandManager.nameToCommandMap.entries.firstOrNull { (names, _) ->
|
||||
this.allNames.any { it.contentEquals(names) }
|
||||
}?.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册这个指令. 若指令未注册, 返回 `false`
|
||||
*/
|
||||
fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
||||
if (!InternalCommandManager.registeredCommands.contains(this)) {
|
||||
return false
|
||||
}
|
||||
InternalCommandManager.registeredCommands.remove(this)
|
||||
for (name in this.allNames) {
|
||||
InternalCommandManager.nameToCommandMap.entries.removeIf {
|
||||
it.key.contentEquals(this.fullName)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并执行一个指令
|
||||
* @param args 接受 [String] 或 [Message]
|
||||
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
||||
*/
|
||||
suspend fun CommandSender.executeCommand(vararg args: Any): Boolean {
|
||||
return args.flattenCommandComponents().toList().executeCommand(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并执行一个指令
|
||||
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
||||
*/
|
||||
suspend fun MessageChain.executeAsCommand(sender: CommandSender): Boolean {
|
||||
return this.flattenCommandComponents().toList().executeCommand(sender)
|
||||
}
|
||||
|
||||
suspend fun CommandSender.execute(command: Command, args: CommandArgs): Boolean = with(command) {
|
||||
checkArgs(args)
|
||||
return onCommand(this@execute, args)
|
||||
}
|
||||
|
||||
suspend fun Command.execute(sender: CommandSender, args: CommandArgs): Boolean = sender.execute(this, args)
|
||||
suspend fun Command.execute(sender: CommandSender, vararg args: Any): Boolean = sender.execute(this, args)
|
||||
suspend fun CommandSender.execute(command: Command, vararg args: Any): Boolean = command.execute(this, args)
|
||||
|
||||
|
||||
internal suspend fun List<Any>.executeCommand(sender: CommandSender): Boolean {
|
||||
val command = InternalCommandManager.matchCommand(this) ?: return false
|
||||
return command.onCommand(sender, CommandArgs.parseFrom(command, sender, this.drop(command.fullName.size)))
|
||||
}
|
||||
|
||||
internal infix fun CommandFullName.matchesBeginning(list: List<Any>): Boolean {
|
||||
this.forEachIndexed { index, any ->
|
||||
if (list[index] != any) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal object InternalCommandManager {
|
||||
@JvmField
|
||||
internal val registeredCommands: MutableList<Command> = mutableListOf()
|
||||
|
||||
@JvmField
|
||||
internal val nameToCommandMap: TreeMap<CommandFullName, Command> = TreeMap(Comparator.comparingInt { it.size })
|
||||
|
||||
@JvmField
|
||||
internal val modifyLock = ReentrantLock()
|
||||
|
||||
|
||||
internal fun matchCommand(splitted: List<Any>): Command? {
|
||||
nameToCommandMap.entries.forEach {
|
||||
if (it.key matchesBeginning splitted) return it.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
@ -20,17 +20,27 @@ data class CommandParam<T : Any>(
|
||||
val type: KClass<T> // exact type
|
||||
) {
|
||||
constructor(name: String?, type: KClass<T>, parser: CommandArgParser<T>) : this(name, type) {
|
||||
this.parser = parser
|
||||
this._overrideParser = parser
|
||||
}
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@JvmField
|
||||
internal var parser: CommandArgParser<T>? = null
|
||||
internal var _overrideParser: CommandArgParser<T>? = null
|
||||
|
||||
|
||||
/**
|
||||
* 覆盖的 [CommandArgParser].
|
||||
*
|
||||
* 如果非 `null`, 将不会从 [CommandParserContext] 寻找 [CommandArgParser]
|
||||
*
|
||||
* @see Command.parserFor
|
||||
*/
|
||||
val overrideParser: CommandArgParser<T>? get() = parser
|
||||
val overrideParser: CommandArgParser<T>? get() = _overrideParser
|
||||
}
|
||||
|
||||
fun <T : Any> CommandParam<T>.parserFrom(command: Command): CommandArgParser<T>? = command.parserFor(this)
|
||||
|
||||
fun <T : Any> CommandParam<T>.parserFrom(descriptor: CommandDescriptor): CommandArgParser<T>? =
|
||||
descriptor.parserFor(this)
|
||||
|
||||
fun <T : Any> CommandParam<T>.parserFrom(context: CommandParserContext): CommandArgParser<T>? = context.parserFor(this)
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.command.AbstractCommandParserContext.Node
|
||||
import net.mamoe.mirai.console.command.AbstractCommandParserContext.ParserPair
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
@ -21,6 +21,7 @@ import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* [KClass] 到 [CommandArgParser] 的匹配
|
||||
* @see AbstractCommandParserContext
|
||||
*/
|
||||
interface CommandParserContext {
|
||||
operator fun <T : Any> get(klass: KClass<T>): CommandArgParser<T>?
|
||||
@ -44,7 +45,14 @@ interface CommandParserContext {
|
||||
})
|
||||
}
|
||||
|
||||
fun <T : Any> CommandParserContext.parserFor(param: CommandParam<T>): CommandArgParser<T>? = this[param.type]
|
||||
fun <T : Any> CommandParserContext.parserFor(param: CommandParam<T>): CommandArgParser<T>? =
|
||||
param.overrideParser ?: this[param.type]
|
||||
|
||||
fun <T : Any> CommandDescriptor.parserFor(param: CommandParam<T>): CommandArgParser<T>? =
|
||||
param.overrideParser ?: this.context.parserFor(param)
|
||||
|
||||
fun <T : Any> Command.parserFor(param: CommandParam<T>): CommandArgParser<T>? =
|
||||
param.overrideParser ?: this.descriptor.parserFor(param)
|
||||
|
||||
/**
|
||||
* 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
|
||||
@ -56,8 +64,8 @@ operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandP
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open class AbstractCommandParserContext(val list: List<Node<*>>) : CommandParserContext {
|
||||
class Node<T : Any>(
|
||||
open class AbstractCommandParserContext(val list: List<ParserPair<*>>) : CommandParserContext {
|
||||
class ParserPair<T : Any>(
|
||||
val klass: KClass<T>,
|
||||
val parser: CommandArgParser<T>
|
||||
)
|
||||
@ -92,10 +100,10 @@ inline fun CommandParserContext(block: CommandParserContextBuilder.() -> Unit):
|
||||
/**
|
||||
* @see CommandParserContext
|
||||
*/
|
||||
class CommandParserContextBuilder : MutableList<Node<*>> by mutableListOf() {
|
||||
class CommandParserContextBuilder : MutableList<ParserPair<*>> by mutableListOf() {
|
||||
@JvmName("add")
|
||||
inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): Node<*> =
|
||||
Node(this, parser)
|
||||
inline infix fun <T : Any> KClass<T>.with(parser: CommandArgParser<T>): ParserPair<*> =
|
||||
ParserPair(this, parser)
|
||||
|
||||
/**
|
||||
* 添加一个指令解析器
|
||||
@ -104,7 +112,7 @@ class CommandParserContextBuilder : MutableList<Node<*>> by mutableListOf() {
|
||||
@LowPriorityInOverloadResolution
|
||||
inline infix fun <T : Any> KClass<T>.with(
|
||||
crossinline parser: CommandArgParser<T>.(s: String, sender: CommandSender) -> T
|
||||
): Node<*> = Node(this, CommandArgParser(parser))
|
||||
): ParserPair<*> = ParserPair(this, CommandArgParser(parser))
|
||||
|
||||
/**
|
||||
* 添加一个指令解析器
|
||||
@ -112,7 +120,7 @@ class CommandParserContextBuilder : MutableList<Node<*>> by mutableListOf() {
|
||||
@JvmSynthetic
|
||||
inline infix fun <T : Any> KClass<T>.with(
|
||||
crossinline parser: CommandArgParser<T>.(s: String) -> T
|
||||
): Node<*> = Node(this, CommandArgParser { s: String, _: CommandSender -> parser(s) })
|
||||
): ParserPair<*> = ParserPair(this, CommandArgParser { s: String, _: CommandSender -> parser(s) })
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,7 +11,6 @@ package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
@ -58,11 +57,13 @@ abstract class AbstractCommandSender : CommandSender {
|
||||
*/
|
||||
object ConsoleCommandSender : AbstractCommandSender() {
|
||||
override suspend fun sendMessage(messageChain: Message) {
|
||||
MiraiConsole.logger("[Command]", 0, messageChain.toString())
|
||||
TODO()
|
||||
// MiraiConsole.logger("[Command]", 0, messageChain.toString())
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String) {
|
||||
MiraiConsole.logger("[Command]", 0, message)
|
||||
TODO()
|
||||
// MiraiConsole.logger("[Command]", 0, message)
|
||||
}
|
||||
|
||||
override suspend fun flushMessage() {
|
||||
|
@ -9,22 +9,26 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.Bot.Companion.botInstances
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.plugins.PluginManager
|
||||
import net.mamoe.mirai.console.utils.addManager
|
||||
import net.mamoe.mirai.console.utils.checkManager
|
||||
import net.mamoe.mirai.console.utils.managers
|
||||
import net.mamoe.mirai.console.utils.removeManager
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
import net.mamoe.mirai.getFriendOrNull
|
||||
import net.mamoe.mirai.message.GroupMessageEvent
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.util.*
|
||||
suspend fun main() {
|
||||
ConsoleCommandSender.execute(DefaultCommands.Test, "test")
|
||||
}
|
||||
|
||||
internal object DefaultCommands {
|
||||
|
||||
object Test : ConsoleCommand(
|
||||
CommandDescriptor("test") {
|
||||
param<String>()
|
||||
}
|
||||
) {
|
||||
override suspend fun onCommand(sender: CommandSender, args: CommandArgs): Boolean {
|
||||
val s = args.getReified<String>()
|
||||
sender.sendMessage(s)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/**
|
||||
* Some defaults commands are recommend to be replaced by plugin provided commands
|
||||
@ -369,3 +373,5 @@ internal object DefaultCommands {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
@ -12,13 +12,12 @@
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.events.EventListener
|
||||
import net.mamoe.mirai.console.scheduler.PluginScheduler
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -28,15 +27,17 @@ import kotlin.coroutines.EmptyCoroutineContext
|
||||
* 所有插件的基类
|
||||
*/
|
||||
abstract class PluginBase
|
||||
@JvmOverloads constructor(coroutineContext: CoroutineContext = EmptyCoroutineContext) : CoroutineScope {
|
||||
@JvmOverloads constructor(coroutineContext: CoroutineContext = EmptyCoroutineContext) : CoroutineScope, CommandOwner {
|
||||
final override val coroutineContext: CoroutineContext = coroutineContext + SupervisorJob()
|
||||
|
||||
/**
|
||||
* 插件被分配的数据目录。数据目录会与插件名称同名。
|
||||
*/
|
||||
val dataFolder: File by lazy {
|
||||
TODO()
|
||||
/*
|
||||
File(PluginManager.pluginsPath + "/" + PluginManager.lastPluginName)
|
||||
.also { it.mkdir() }
|
||||
.also { it.mkdir() }*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,13 +83,15 @@ abstract class PluginBase
|
||||
* 插件的日志
|
||||
*/
|
||||
val logger: MiraiLogger by lazy {
|
||||
TODO()
|
||||
/*
|
||||
SimpleLogger("Plugin $pluginName") { priority, message, e ->
|
||||
val identityString = "[${pluginName}]"
|
||||
MiraiConsole.logger(priority, identityString, 0, message)
|
||||
if (e != null) {
|
||||
MiraiConsole.logger(priority, identityString, 0, e)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,10 +101,13 @@ abstract class PluginBase
|
||||
return try {
|
||||
this.javaClass.classLoader.getResourceAsStream(fileName)
|
||||
} catch (e: Exception) {
|
||||
TODO()
|
||||
|
||||
/*
|
||||
PluginManager.getFileInJarByName(
|
||||
this.pluginName,
|
||||
fileName
|
||||
)
|
||||
)*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,18 +14,18 @@ package net.mamoe.mirai.console.plugins
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.description
|
||||
import net.mamoe.mirai.console.command.unregisterAllCommands
|
||||
import net.mamoe.mirai.console.encodeToString
|
||||
import net.mamoe.mirai.utils.LockFreeLinkedList
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URL
|
||||
import java.util.jar.JarFile
|
||||
|
||||
val PluginBase.description: PluginDescription get() = PluginManager.getPluginDescription(this)
|
||||
val PluginBase.description: PluginDescription get() = TODO()
|
||||
|
||||
object PluginManagerOld {
|
||||
/**
|
||||
@ -53,9 +53,9 @@ object PluginManagerOld {
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun disablePlugins(throwable: CancellationException? = null) {
|
||||
CommandManager.clearPluginsCommands()
|
||||
pluginsSequence.forEach {
|
||||
it.disable(throwable)
|
||||
pluginsSequence.forEach { plugin ->
|
||||
plugin.unregisterAllCommands()
|
||||
plugin.disable(throwable)
|
||||
}
|
||||
nameToPluginBaseMap.clear()
|
||||
pluginDescriptions.clear()
|
||||
@ -87,10 +87,7 @@ object PluginManagerOld {
|
||||
File(it).mkdirs()
|
||||
}
|
||||
|
||||
private val logger = SimpleLogger("Plugin Manager") { p, message, e ->
|
||||
MiraiConsole.logger(p, "[Plugin Manager]", 0, message)
|
||||
MiraiConsole.logger(p, "[Plugin Manager]", 0, e)
|
||||
}
|
||||
private val logger = MiraiConsole.newLogger("Plugin Manager")
|
||||
|
||||
/**
|
||||
* 加载成功的插件, 名字->插件
|
||||
@ -351,7 +348,7 @@ object PluginManagerOld {
|
||||
plugin: PluginBase,
|
||||
exception: CancellationException? = null
|
||||
) {
|
||||
CommandManager.clearPluginCommands(plugin)
|
||||
plugin.unregisterAllCommands()
|
||||
plugin.disable(exception)
|
||||
nameToPluginBaseMap.remove(plugin.pluginName)
|
||||
pluginDescriptions.remove(plugin.pluginName)
|
||||
|
@ -10,7 +10,6 @@
|
||||
package net.mamoe.mirai.console.plugins
|
||||
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
@ -18,10 +17,7 @@ internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
|
||||
private val loggerName = "PluginsLoader"
|
||||
private val pluginLoaders = linkedMapOf<String, PluginClassLoader>()
|
||||
private val classesCache = mutableMapOf<String, Class<*>>()
|
||||
private val logger = SimpleLogger(loggerName) { p, message, e ->
|
||||
MiraiConsole.logger(p, "[${loggerName}]", 0, message)
|
||||
MiraiConsole.logger(p, "[${loggerName}]", 0, e)
|
||||
}
|
||||
private val logger = MiraiConsole.newLogger(loggerName)
|
||||
|
||||
/**
|
||||
* 清除所有插件加载器
|
||||
|
@ -27,7 +27,7 @@ interface MiraiConsoleFrontEnd {
|
||||
*/
|
||||
val pluginCenter: PluginCenter get() = CuiPluginCenter
|
||||
|
||||
fun loggerFor(identity: Long): MiraiLogger
|
||||
fun loggerFor(identity: String?): MiraiLogger
|
||||
|
||||
/**
|
||||
* 让 UI 层准备接受新增的一个BOT
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
package net.mamoe.mirai.console.graphical
|
||||
|
||||
import kotlinx.coroutines.cancel
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import tornadofx.launch
|
||||
import kotlin.concurrent.thread
|
||||
@ -24,7 +25,7 @@ class MiraiConsoleGraphicalLoader {
|
||||
this.coreVersion = coreVersion
|
||||
this.consoleVersion = consoleVersion
|
||||
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
||||
MiraiConsole.stop()
|
||||
MiraiConsole.cancel()
|
||||
})
|
||||
launch<MiraiGraphicalUI>()
|
||||
}
|
||||
|
@ -23,41 +23,46 @@ pluginManagement {
|
||||
|
||||
rootProject.name = 'mirai-console'
|
||||
|
||||
def onlyBackEnd = true
|
||||
|
||||
include(':mirai-console')
|
||||
project(':mirai-console').dir = file("backend/mirai-console")
|
||||
|
||||
|
||||
include(':mirai-console-pure')
|
||||
project(':mirai-console-pure').dir = file("frontend/mirai-console-pure")
|
||||
if (!onlyBackEnd) {
|
||||
|
||||
include(':mirai-console-terminal')
|
||||
project(':mirai-console-terminal').dir = file("frontend/mirai-console-terminal")
|
||||
include(':mirai-console-pure')
|
||||
project(':mirai-console-pure').dir = file("frontend/mirai-console-pure")
|
||||
|
||||
try{
|
||||
def javaVersion = System.getProperty("java.version")
|
||||
def versionPos = javaVersion.indexOf(".")
|
||||
def javaVersionNum = javaVersion.substring(0, 1).toInteger()
|
||||
include(':mirai-console-terminal')
|
||||
project(':mirai-console-terminal').dir = file("frontend/mirai-console-terminal")
|
||||
|
||||
if (javaVersion.startsWith("1.")) {
|
||||
javaVersionNum = javaVersion.substring(2, 3).toInteger()
|
||||
} else {
|
||||
if (versionPos==-1) versionPos = javaVersion.indexOf("-")
|
||||
if (versionPos==-1){
|
||||
println("jdk version unknown")
|
||||
}else{
|
||||
javaVersionNum = javaVersion.substring(0, versionPos).toInteger()
|
||||
try {
|
||||
def javaVersion = System.getProperty("java.version")
|
||||
def versionPos = javaVersion.indexOf(".")
|
||||
def javaVersionNum = javaVersion.substring(0, 1).toInteger()
|
||||
|
||||
if (javaVersion.startsWith("1.")) {
|
||||
javaVersionNum = javaVersion.substring(2, 3).toInteger()
|
||||
} else {
|
||||
if (versionPos == -1) versionPos = javaVersion.indexOf("-")
|
||||
if (versionPos == -1) {
|
||||
println("jdk version unknown")
|
||||
} else {
|
||||
javaVersionNum = javaVersion.substring(0, versionPos).toInteger()
|
||||
}
|
||||
}
|
||||
if (javaVersionNum >= 9) {
|
||||
include(':mirai-console-graphical')
|
||||
project(':mirai-console-graphical').dir = file("frontend/mirai-console-graphical")
|
||||
} else {
|
||||
println("jdk版本为 " + javaVersionNum)
|
||||
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 请使用JDK 9以上版本引入模块 `:mirai-console-graphical`\n")
|
||||
}
|
||||
}
|
||||
if (javaVersionNum >= 9) {
|
||||
include(':mirai-console-graphical')
|
||||
project(':mirai-console-graphical').dir = file("frontend/mirai-console-graphical")
|
||||
} else {
|
||||
println("jdk版本为 "+ javaVersionNum)
|
||||
println("当前使用的 JDK 版本为 ${System.getProperty("java.version")}, 请使用JDK 9以上版本引入模块 `:mirai-console-graphical`\n")
|
||||
}
|
||||
|
||||
}catch(Exception ignored){
|
||||
println("无法确定 JDK 版本, 将不会引入 `:mirai-console-graphical`")
|
||||
} catch (Exception ignored) {
|
||||
println("无法确定 JDK 版本, 将不会引入 `:mirai-console-graphical`")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user