Add allRegisteredCommands;

Add val MiraiConsole.job;
Implement command listening;
Add simple built-in commands
This commit is contained in:
Him188 2020-06-29 16:17:12 +08:00
parent 108e425082
commit 2fd7c6aec7
6 changed files with 188 additions and 12 deletions

View File

@ -17,7 +17,10 @@ import kotlinx.coroutines.Job
import kotlinx.io.charsets.Charset
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
import net.mamoe.mirai.console.command.BuiltInCommands
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.command.internal.InternalCommandManager
import net.mamoe.mirai.console.command.primaryName
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
@ -75,6 +78,11 @@ interface MiraiConsole : CoroutineScope {
companion object INSTANCE : MiraiConsole by MiraiConsoleInternal
}
/**
* 获取 [MiraiConsole] [Job]
*/
val MiraiConsole.job: Job
get() = this.coroutineContext[Job] ?: error("Internal error: Job not found in MiraiConsole.coroutineContext")
//// internal
@ -132,10 +140,14 @@ internal object MiraiConsoleInternal : CoroutineScope, IMiraiConsole, MiraiConso
if (coroutineContext[Job] == null) {
throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
}
this.coroutineContext[Job]!!.invokeOnCompletion {
job.invokeOnCompletion {
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
}
BuiltInCommands.registerAll()
mainLogger.info { "Preparing built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
InternalCommandManager.commandListener // start
mainLogger.info { "Loading plugins..." }
PluginManager.loadEnablePlugins()
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }

View File

@ -9,12 +9,114 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.Bot
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.job
import net.mamoe.mirai.console.stacktraceString
import net.mamoe.mirai.event.selectMessagesUnit
import kotlin.concurrent.thread
import kotlin.system.exitProcess
/**
* 添加一个 [Bot] 实例到全局 Bot 列表, 但不登录.
*/
fun MiraiConsole.addBot(id: Long, password: String): Bot {
return Bot(id, password) {
fileBasedDeviceInfo()
this.loginSolver = this@addBot.frontEnd.createLoginSolver()
redirectNetworkLogToDirectory()
redirectBotLogToDirectory()
}
}
interface BuiltInCommand : Command
@ConsoleExperimentalAPI
object BuiltInCommands
@Suppress("unused")
object BuiltInCommands {
val all: Array<out Command> by lazy {
this::class.nestedClasses.mapNotNull { it.objectInstance as? Command }.toTypedArray()
}
internal fun registerAll() {
BuiltInCommands::class.nestedClasses.forEach {
(it.objectInstance as? Command)?.register()
}
}
object Help : SimpleCommand(
ConsoleCommandOwner, "help", "?",
description = "Gets help about the console."
) {
init {
Runtime.getRuntime().addShutdownHook(thread(false) {
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
})
}
@Handler
suspend fun CommandSender.handle() {
sendMessage("现在有指令: ${allRegisteredCommands.joinToString { it.primaryName }}")
sendMessage("帮助还没写, 将就一下")
}
}
object Stop : SimpleCommand(
ConsoleCommandOwner, "stop", "shutdown", "exit",
description = "Stop the whole world."
) {
init {
Runtime.getRuntime().addShutdownHook(thread(false) {
runBlocking { Stop.execute(ConsoleCommandSender.instance) }
})
}
@Handler
suspend fun CommandSender.handle() {
sendMessage("Stopping mirai-console")
kotlin.runCatching {
MiraiConsole.job.cancel()
}.fold(
onSuccess = { sendMessage("mirai-console stopped successfully.") },
onFailure = {
MiraiConsole.mainLogger.error(it)
sendMessage(it.localizedMessage ?: it.message ?: it.toString())
}
)
exitProcess(0)
}
}
object Login : SimpleCommand(
ConsoleCommandOwner, "login",
description = "Log in a bot account."
) {
@Handler
suspend fun CommandSender.handle(id: Long, password: String) {
sendMessage(
kotlin.runCatching {
MiraiConsole.addBot(id, password).alsoLogin()
}.fold(
onSuccess = { "${it.nick} ($id) Login succeed" },
onFailure = { throwable ->
"Login failed: ${throwable.localizedMessage ?: throwable.message}" +
if (this is MessageEventContextAware<*>) {
this.fromEvent.selectMessagesUnit {
"stacktrace" reply {
throwable.stacktraceString
}
}
"test"
} else ""
}
)
)
}
}
}
/*

View File

@ -53,6 +53,12 @@ object ConsoleCommandOwner : CommandOwner()
*/
val CommandOwner.registeredCommands: List<Command> get() = InternalCommandManager.registeredCommands.filter { it.owner == this }
/**
* 获取所有已经注册了指令列表.
* @see JCommandManager.getRegisteredCommands Java 方法
*/
val allRegisteredCommands: List<Command> get() = InternalCommandManager.registeredCommands.toList() // copy
/**
* 指令前缀, '/'
* @see JCommandManager.getCommandPrefix Java 方法

View File

@ -14,9 +14,10 @@ package net.mamoe.mirai.console.command
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsoleInternal
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
import net.mamoe.mirai.console.utils.JavaFriendlyAPI
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.*
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.PlainText
@ -83,6 +84,12 @@ fun User.asCommandSender(): UserCommandSender {
}
}
/**
* 表示由 [MessageEvent] 触发的指令
*/
interface MessageEventContextAware<E : MessageEvent> : MessageEventExtensions<User, Contact> {
val fromEvent: E
}
/**
* 代表一个用户私聊机器人执行指令
@ -99,7 +106,7 @@ sealed class UserCommandSender : CommandSender, BotAwareCommandSender {
*/
abstract val subject: Contact
final override val bot: Bot get() = user.bot
override val bot: Bot get() = user.bot
final override suspend fun sendMessage(message: Message) {
subject.sendMessage(message)
@ -110,15 +117,46 @@ sealed class UserCommandSender : CommandSender, BotAwareCommandSender {
* 代表一个用户私聊机器人执行指令
* @see Friend.asCommandSender
*/
class FriendCommandSender(override val user: Friend) : UserCommandSender() {
open class FriendCommandSender(final override val user: Friend) : UserCommandSender() {
override val subject: Contact get() = user
}
/**
* 代表一个用户私聊机器人执行指令
* @see Friend.asCommandSender
*/
class FriendCommandSenderOnMessage(override val fromEvent: FriendMessageEvent) : FriendCommandSender(fromEvent.sender),
MessageEventContextAware<FriendMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot
}
/**
* 代表一个群成员执行指令.
* @see Member.asCommandSender
*/
open class MemberCommandSender(final override val user: Member) : UserCommandSender() {
inline val group: Group get() = user.group
override val subject: Contact get() = group
}
/**
* 代表一个群成员在群内执行指令.
* @see Member.asCommandSender
*/
class MemberCommandSender(override val user: Member) : UserCommandSender() {
inline val group: Group get() = user.group
override val subject: Contact get() = group
class MemberCommandSenderOnMessage(override val fromEvent: GroupMessageEvent) : MemberCommandSender(fromEvent.sender),
MessageEventContextAware<GroupMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot
}
/**
* 代表一个群成员通过临时会话私聊机器人执行指令.
* @see Member.asCommandSender
*/
@ConsoleExperimentalAPI
class TempCommandSenderOnMessage(override val fromEvent: TempMessageEvent) : MemberCommandSender(fromEvent.sender),
MessageEventContextAware<TempMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
override val subject: Contact get() = super.subject
override val bot: Bot get() = super.bot
}

View File

@ -9,9 +9,15 @@
package net.mamoe.mirai.console.command.internal
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.job
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.event.Listener
import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.message.MessageEvent
import java.util.concurrent.locks.ReentrantLock
@ -22,7 +28,7 @@ internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
return true
}
internal object InternalCommandManager {
internal object InternalCommandManager : CoroutineScope by CoroutineScope(MiraiConsole.job) {
const val COMMAND_PREFIX = "/"
@JvmField
@ -56,6 +62,18 @@ internal object InternalCommandManager {
}
return optionalPrefixCommandMap[rawCommand.toLowerCase()]
}
internal val commandListener: Listener<MessageEvent> by lazy {
@Suppress("RemoveExplicitTypeArguments")
subscribeAlways<MessageEvent>(
concurrency = Listener.ConcurrencyKind.CONCURRENT,
priority = Listener.EventPriority.HIGH
) {
if (this.sender.asCommandSender().executeCommand(message) != null) {
intercept()
}
}
}
}
internal infix fun Array<out String>.intersectsIgnoringCase(other: Array<out String>): Boolean {

View File

@ -9,7 +9,7 @@
object Versions {
const val core = "1.1-EA"
const val console = "1.1-dev-1"
const val console = "1.0-dev-1"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = "0.1.0"