Review: misc improvements

This commit is contained in:
Him188 2020-03-21 15:52:27 +08:00
parent 51ceafdfe4
commit 7fb9056572
6 changed files with 218 additions and 156 deletions

View File

@ -6,7 +6,7 @@ import javafx.stage.Modality
import javafx.stage.StageStyle import javafx.stage.StageStyle
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.graphical.model.* import net.mamoe.mirai.console.graphical.model.*
import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment
import net.mamoe.mirai.console.plugins.PluginManager import net.mamoe.mirai.console.plugins.PluginManager
@ -30,10 +30,10 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
val consoleInfo = ConsoleInfo() val consoleInfo = ConsoleInfo()
fun login(qq: String, psd: String) { fun login(qq: String, psd: String) {
MiraiConsole.CommandProcessor.runConsoleCommandBlocking("/login $qq $psd") CommandManager.runConsoleCommand("/login $qq $psd")
} }
fun sendCommand(command: String) = MiraiConsole.CommandProcessor.runConsoleCommandBlocking(command) fun sendCommand(command: String) = CommandManager.runConsoleCommand(command)
override fun pushLog(identity: Long, message: String) = Platform.runLater { override fun pushLog(identity: Long, message: String) = Platform.runLater {
fun ObservableList<*>.trim() { fun ObservableList<*>.trim() {

View File

@ -7,90 +7,53 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("NOTHING_TO_INLINE") @file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugins.PluginBase import net.mamoe.mirai.console.plugins.PluginBase
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.SimpleLogger.LogPriority
interface CommandSender {
/**
* 立刻发送一条Message
*/
suspend fun sendMessage(messageChain: MessageChain)
suspend fun sendMessage(message: String)
/**
* 写入要发送的内容 所有内容最后会被以一条发出
*/
fun appendMessage(message: String)
fun sendMessageBlocking(messageChain: MessageChain) = runBlocking { sendMessage(messageChain) }
fun sendMessageBlocking(message: String) = runBlocking { sendMessage(message) }
}
abstract class CommandSenderImpl : CommandSender {
internal val builder = StringBuilder()
override fun appendMessage(message: String) {
builder.append(message).append("\n")
}
internal open suspend fun flushMessage() {
if (!builder.isEmpty()) {
sendMessage(builder.toString().removeSuffix("\n"))
}
}
}
object ConsoleCommandSender : CommandSenderImpl() {
override suspend fun sendMessage(messageChain: MessageChain) {
MiraiConsole.logger("[Command]", 0, messageChain.toString())
}
override suspend fun sendMessage(message: String) {
MiraiConsole.logger("[Command]", 0, message)
}
override suspend fun flushMessage() {
super.flushMessage()
builder.clear()
}
}
open class ContactCommandSender(val contact: Contact) : CommandSenderImpl() {
override suspend fun sendMessage(messageChain: MessageChain) {
contact.sendMessage(messageChain)
}
override suspend fun sendMessage(message: String) {
contact.sendMessage(message)
}
}
/**
* 指令
*
* @see register 注册这个指令
* @see registerCommand 注册指令 DSL
*/
interface Command { interface Command {
/**
* 指令主名称
*/
val name: String val name: String
/**
* 别名
*/
val alias: List<String> val alias: List<String>
/**
* 描述, 将会显示在 "/help" 指令中
*/
val description: String val description: String
/**
* 用法说明
*/
val usage: String val usage: String
suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean
} }
/**
* 注册这个指令
*/
inline fun Command.register() = CommandManager.register(this) inline fun Command.register() = CommandManager.register(this)
/**
fun registerCommand(builder: CommandBuilder.() -> Unit): Command { * 构造并注册一个指令
*/
inline fun registerCommand(builder: CommandBuilder.() -> Unit): Command {
return CommandBuilder().apply(builder).register() return CommandBuilder().apply(builder).register()
} }
@ -104,8 +67,9 @@ abstract class BlockingCommand(
override val usage: String = "" override val usage: String = ""
) : Command { ) : Command {
/** /**
* 最高优先级监听器 * 最高优先级监听器.
* 如果 return `false`, 这次指令不会被 [PluginBase] 的全局 onCommand 监听器监听 *
* 指令调用将优先触发 [Command.onCommand], 若该函数返回 `false`, 则不会调用 [PluginBase.onCommand]
* */ * */
final override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean { final override suspend fun onCommand(sender: CommandSender, args: List<String>): Boolean {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
@ -116,7 +80,27 @@ abstract class BlockingCommand(
abstract fun onCommandBlocking(sender: CommandSender, args: List<String>): Boolean abstract fun onCommandBlocking(sender: CommandSender, args: List<String>): Boolean
} }
class AnonymousCommand internal constructor( /**
* @see registerCommand
*/
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
}
}
// internal
internal class AnonymousCommand internal constructor(
override val name: String, override val name: String,
override val alias: List<String>, override val alias: List<String>,
override val description: String, override val description: String,
@ -128,19 +112,8 @@ class AnonymousCommand internal constructor(
} }
} }
class CommandBuilder internal constructor() { @PublishedApi
var name: String? = null internal fun CommandBuilder.register(): AnonymousCommand {
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
}
}
private fun CommandBuilder.register(): AnonymousCommand {
if (name == null || onCommand == null) { if (name == null || onCommand == null) {
error("CommandBuilder not complete") error("CommandBuilder not complete")
} }

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("unused") @file:Suppress("unused", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
@ -18,7 +18,6 @@ import net.mamoe.mirai.console.plugins.PluginBase
import net.mamoe.mirai.console.plugins.PluginManager import net.mamoe.mirai.console.plugins.PluginManager
import java.util.concurrent.Executors import java.util.concurrent.Executors
object CommandManager : Job by { object CommandManager : Job by {
GlobalScope.launch(start = CoroutineStart.LAZY) { GlobalScope.launch(start = CoroutineStart.LAZY) {
processCommandQueue() processCommandQueue()
@ -28,6 +27,11 @@ object CommandManager : Job by {
val commands: Collection<Command> get() = registeredCommand.values val commands: Collection<Command> get() = registeredCommand.values
/**
* 注册这个指令.
*
* @throws IllegalStateException 当已注册的指令与 [command] 重名时
*/
fun register(command: Command) { fun register(command: Command) {
val allNames = mutableListOf(command.name).also { it.addAll(command.alias) } val allNames = mutableListOf(command.name).also { it.addAll(command.alias) }
allNames.forEach { allNames.forEach {
@ -41,9 +45,10 @@ object CommandManager : Job by {
} }
fun unregister(command: Command) { fun unregister(command: Command) {
(command.alias.asSequence() + command.name).forEach { command.alias.forEach {
registeredCommand.remove(it) registeredCommand.remove(it)
} }
registeredCommand.remove(command.name)
} }
fun unregister(commandName: String): Boolean { fun unregister(commandName: String): Boolean {
@ -52,23 +57,65 @@ object CommandManager : Job by {
/** /**
* 指令是单线程运行的 * 最基础的执行指令的方式
* 指令将会被加入队列依次执行
*
* @param sender 指令执行者, 可为 [ConsoleCommandSender] [ContactCommandSender]
*/ */
private val commandDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher() fun runCommand(sender: CommandSender, command: String) {
commandChannel.offer(
FullCommand(sender, command)
)
}
/** /**
* 执行一个指令, 但是如果你想模拟一个指令的执行 * 插队异步执行一个指令并返回 [Deferred]
* 请向下看
* *
* 返回一个指令是否执行成功 * @param sender 指令执行者, 可为 [ConsoleCommandSender] [ContactCommandSender]
* @see PluginBase.runCommandAsync 扩展
*/ */
internal suspend fun processCommand(sender: CommandSender, fullCommand: String):Boolean { fun runCommandAsync(pluginBase: PluginBase, sender: CommandSender, command: String): Deferred<Boolean> {
return withContext(commandDispatcher) { return pluginBase.async {
_processCommand(sender, fullCommand) processCommand(sender, command)
} }
} }
private suspend fun _processCommand(sender: CommandSender, fullCommand: String): Boolean { /**
* 插队执行一个指令并返回 [Deferred]
*
* @param sender 指令执行者, 可为 [ConsoleCommandSender] [ContactCommandSender]
* @see PluginBase.runCommandAsync 扩展
*/
@Suppress("KDocUnresolvedReference")
suspend fun dispatchCommand(sender: CommandSender, command: String): Boolean {
return processCommand(sender, command)
}
/**
* 阻塞当前线程, 插队执行一个指令
*
* @param sender 指令执行者, 可为 [ConsoleCommandSender] [ContactCommandSender]
*/
// for java
fun dispatchCommandBlocking(sender: CommandSender, command: String): Boolean =
runBlocking { dispatchCommand(sender, command) }
// internal
/**
* 单线程执行指令
*/
private val commandDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
private suspend fun processCommand(sender: CommandSender, fullCommand: String): Boolean {
return withContext(commandDispatcher) {
processCommandImpl(sender, fullCommand)
}
}
private suspend fun processCommandImpl(sender: CommandSender, fullCommand: String): Boolean {
val blocks = fullCommand.split(" ") val blocks = fullCommand.split(" ")
val commandHead = blocks[0].replace("/", "") val commandHead = blocks[0].replace("/", "")
val args = blocks.drop(1) val args = blocks.drop(1)
@ -81,17 +128,16 @@ object CommandManager : Job by {
sender.sendMessage(this.usage) sender.sendMessage(this.usage)
} }
} }
}catch (e: Exception){ } catch (e: Exception) {
sender.sendMessage("在运行指令时出现了未知错误") sender.sendMessage("在运行指令时出现了未知错误")
e.printStackTrace() e.printStackTrace()
false false
}finally { } finally {
(sender as CommandSenderImpl).flushMessage() (sender as CommandSenderImpl).flushMessage()
} }
}?: throw UnknownCommandException(commandHead) } ?: throw UnknownCommandException(commandHead)
} }
internal class FullCommand( internal class FullCommand(
val sender: CommandSender, val sender: CommandSender,
val commandStr: String val commandStr: String
@ -103,62 +149,23 @@ object CommandManager : Job by {
val command = commandChannel.receive() val command = commandChannel.receive()
try { try {
processCommand(command.sender, command.commandStr) processCommand(command.sender, command.commandStr)
}catch (e:UnknownCommandException){ } catch (e: UnknownCommandException) {
command.sender.sendMessage("未知指令 " + command.commandStr) command.sender.sendMessage("未知指令 " + command.commandStr)
}catch (e:Throwable){//should never happen } catch (e: Throwable) {//should never happen
e.printStackTrace() e.printStackTrace()
} }
processCommandQueue() processCommandQueue()
} }
/**
* runCommand()是最基础的执行指令的方式
* 指令将会被加入队列依次执行
* 方法由0.27.0的阻塞模式改为不阻塞(鉴于commandChannel大小无限)
*/
fun runCommand(sender: CommandSender, command: String) {
runBlocking {//it wont be blocking
commandChannel.send(
FullCommand(sender, command)
)
}
}
@Suppress("unused")
fun runConsoleCommand(command: String) = runCommand(ConsoleCommandSender,command)
/**
* runCommandAnsyc()执行一个指令并返回deferred
* 为插队执行
*/
fun runCommandAsync(pluginBase: PluginBase, sender: CommandSender, command: String):Deferred<Boolean>{
return pluginBase.async{
processCommand(sender,command)
}
}
fun runConsoleCommandAsync(pluginBase: PluginBase, command: String):Deferred<Boolean> = runCommandAsync(pluginBase,ConsoleCommandSender,command)
/**
* dispatchCommand()执行一个指令并等到完成
* 为插队执行
*/
suspend fun dispatchCommand(sender: CommandSender,command: String):Boolean{
return processCommand(sender,command)
}
suspend fun dispatchConsoleCommand(command: String):Boolean = dispatchCommand(ConsoleCommandSender,command)
fun dispatchCommandBlocking(sender: CommandSender,command: String):Boolean = runBlocking { dispatchCommand(sender, command) }
fun dispatchConsoleCommandBlocking(command: String):Boolean = runBlocking { dispatchConsoleCommandBlocking(command) }
} }
fun PluginBase.runCommandAsnyc(sender: CommandSender, command: String):Deferred<Boolean> = CommandManager.runCommandAsync(this,sender,command) /**
* 插队异步执行一个指令并返回 [Deferred]
fun PluginBase.runConsoleCommandAsync(command: String):Deferred<Boolean> = CommandManager.runConsoleCommandAsync(this,command) *
* @param sender 指令执行者, 可为 [ConsoleCommandSender] [ContactCommandSender]
* @see PluginBase.runCommandAsync 扩展
*/
fun PluginBase.runCommandAsync(sender: CommandSender, command: String): Deferred<Boolean> =
CommandManager.runCommandAsync(this, sender, command)
class UnknownCommandException(command: String) : Exception("unknown command \"$command\"")
class UnknownCommandException(command: String):Exception("unknown command \"$command\"")

View File

@ -0,0 +1,82 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.command
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.message.data.Message
/**
* 指令发送者
*/
interface CommandSender {
/**
* 立刻发送一条消息
*/
suspend fun sendMessage(messageChain: Message)
suspend fun sendMessage(message: String)
/**
* 写入要发送的内容 所有内容最后会被以一条发出
*/
fun appendMessage(message: String)
fun sendMessageBlocking(messageChain: Message) = runBlocking { sendMessage(messageChain) }
fun sendMessageBlocking(message: String) = runBlocking { sendMessage(message) }
}
abstract class CommandSenderImpl : CommandSender {
internal val builder = StringBuilder()
override fun appendMessage(message: String) {
builder.append(message).append("\n")
}
internal open suspend fun flushMessage() {
if (builder.isNotEmpty()) {
sendMessage(builder.toString().removeSuffix("\n"))
}
}
}
/**
* 控制台指令执行者. 代表由控制台执行指令
*/
object ConsoleCommandSender : CommandSenderImpl() {
override suspend fun sendMessage(messageChain: Message) {
MiraiConsole.logger("[Command]", 0, messageChain.toString())
}
override suspend fun sendMessage(message: String) {
MiraiConsole.logger("[Command]", 0, message)
}
override suspend fun flushMessage() {
super.flushMessage()
builder.clear()
}
}
/**
* 联系人指令执行者. 代表由一个 QQ 用户执行指令
*/
@Suppress("MemberVisibilityCanBePrivate")
open class ContactCommandSender(val contact: Contact) : CommandSenderImpl() {
override suspend fun sendMessage(messageChain: Message) {
contact.sendMessage(messageChain)
}
override suspend fun sendMessage(message: String) {
contact.sendMessage(message)
}
}

View File

@ -15,10 +15,8 @@ import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.Command import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.pure.MiraiConsoleUIPure
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.SimpleLogger import net.mamoe.mirai.utils.SimpleLogger
import net.mamoe.mirai.utils.SimpleLogger.LogPriority
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.net.URLClassLoader import java.net.URLClassLoader
@ -63,7 +61,9 @@ abstract class PluginBase
} }
/** /**
* 当任意指令被使用 * 当任意指令被使用时调用.
*
* 指令调用将优先触发 [Command.onCommand], 若该函数返回 `false`, 则不会调用 [PluginBase.onCommand]
*/ */
open fun onCommand(command: Command, sender: CommandSender, args: List<String>) { open fun onCommand(command: Command, sender: CommandSender, args: List<String>) {

View File

@ -11,7 +11,7 @@ package net.mamoe.mirai.console.pure
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.utils.MiraiConsoleUI import net.mamoe.mirai.console.utils.MiraiConsoleUI
import net.mamoe.mirai.utils.DefaultLoginSolver import net.mamoe.mirai.utils.DefaultLoginSolver
import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.LoginSolver
@ -49,7 +49,7 @@ class MiraiConsoleUIPure : MiraiConsoleUI {
requestStr = input requestStr = input
requesting = false requesting = false
} else { } else {
MiraiConsole.CommandProcessor.runConsoleCommandBlocking(input) CommandManager.runConsoleCommand(input)
} }
} }
} }