mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Redesign CommandSender;
Optimize frontend implementation APIs
This commit is contained in:
parent
2c67d97d58
commit
ae4d5074a5
@ -85,7 +85,7 @@ public interface MiraiConsole : CoroutineScope {
|
||||
* 创建一个 logger
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun newLogger(identity: String?): MiraiLogger
|
||||
public fun createLogger(identity: String?): MiraiLogger
|
||||
|
||||
public companion object INSTANCE : MiraiConsole by MiraiConsoleImplementationBridge {
|
||||
/**
|
||||
|
@ -30,8 +30,18 @@ public interface MiraiConsoleFrontEndDescription {
|
||||
*/
|
||||
public val version: Semver
|
||||
|
||||
/**
|
||||
* 兼容的 [MiraiConsole] 后端版本号
|
||||
*
|
||||
* 如 `Semver("[1.0.0, 2.0.0)", Semver.SemverType.IVY)`
|
||||
*
|
||||
* 返回 `null` 表示禁止 [MiraiConsole] 后端检查版本兼容性.
|
||||
*/
|
||||
public val compatibleBackendVersion: Semver? get() = null
|
||||
|
||||
/**
|
||||
* 返回显示在 [MiraiConsole] 启动时的信息
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun render(): String = "Frontend ${name}: version ${version}, provided by $vendor"
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
@ -20,19 +20,22 @@ import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.LoginSolver
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
||||
/**
|
||||
* 标记一个仅用于 [MiraiConsole] 前端实现的 API. 这些 API 只应由前端实现者使用, 而不应该被插件或其他调用者使用.
|
||||
* 标记一个仅用于 [MiraiConsole] 前端实现的 API.
|
||||
*
|
||||
* 这些 API 只应由前端实现者使用, 而不应该被插件或其他调用者使用.
|
||||
*/
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@ -41,42 +44,81 @@ import kotlin.coroutines.CoroutineContext
|
||||
public annotation class ConsoleFrontEndImplementation
|
||||
|
||||
/**
|
||||
* 由前端实现这个接口
|
||||
* 实现 [MiraiConsole] 的接口
|
||||
*
|
||||
* @see MiraiConsoleImplementation.start
|
||||
* **注意**: 随着 Console 的更新, 在版本号 `x.y.z` 的 `y` 修改时此接口可能就会变动. 意味着前端实现着需要跟随 Console 更新.
|
||||
*
|
||||
* @see MiraiConsoleImplementation.start 启动
|
||||
*/
|
||||
@ConsoleFrontEndImplementation
|
||||
public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
/**
|
||||
* [MiraiConsole] 的 [CoroutineScope.coroutineContext]
|
||||
* [MiraiConsole] 的 [CoroutineScope.coroutineContext], 必须拥有如下元素
|
||||
*
|
||||
* - [Job]: 用于管理整个 [MiraiConsole] 的生命周期. 当此 [Job] 被 [Job.cancel] 后, [MiraiConsole] 就会结束.
|
||||
* - [CoroutineExceptionHandler]: 用于处理 [MiraiConsole] 所有协程抛出的 **未被捕捉** 的异常. 不是所有异常都会被传递到这里.
|
||||
*/
|
||||
public override val coroutineContext: CoroutineContext
|
||||
|
||||
/**
|
||||
* Console 运行根目录
|
||||
* @see MiraiConsole.rootPath
|
||||
* Console 运行根目录绝对路径 (否则可能会被一些 native 插件覆盖相对路径)
|
||||
* @see MiraiConsole.rootPath 获取更多信息
|
||||
*/
|
||||
public val rootPath: Path
|
||||
|
||||
/**
|
||||
* Console 前端接口
|
||||
* 本前端实现的描述信息
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val frontEndDescription: MiraiConsoleFrontEndDescription
|
||||
|
||||
/**
|
||||
* 与前端交互所使用的 Logger
|
||||
*/
|
||||
public val mainLogger: MiraiLogger
|
||||
|
||||
/**
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader].
|
||||
*
|
||||
* @return 不可变的 [List]
|
||||
* @return 不可变的 [List], [Collections.unmodifiableList]
|
||||
*/
|
||||
public val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
public val consoleCommandSender: ConsoleCommandSender
|
||||
/**
|
||||
* 由 Kotlin 用户实现
|
||||
*
|
||||
* @see [ConsoleCommandSender]
|
||||
*/
|
||||
@ConsoleFrontEndImplementation
|
||||
public interface ConsoleCommandSenderImpl {
|
||||
@JvmSynthetic
|
||||
public suspend fun sendMessage(message: Message)
|
||||
|
||||
@JvmSynthetic
|
||||
public suspend fun sendMessage(message: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* 由 Java 用户实现
|
||||
*
|
||||
* @see [ConsoleCommandSender]
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
@ConsoleFrontEndImplementation
|
||||
public interface JConsoleCommandSenderImpl : ConsoleCommandSenderImpl {
|
||||
@JvmName("sendMessage")
|
||||
public fun sendMessageJ(message: Message)
|
||||
|
||||
@JvmName("sendMessage")
|
||||
public fun sendMessageJ(message: String)
|
||||
|
||||
|
||||
@JvmSynthetic
|
||||
@JvmDefault
|
||||
public override suspend fun sendMessage(message: Message): Unit =
|
||||
withContext(Dispatchers.IO) { sendMessageJ(message) }
|
||||
|
||||
@JvmSynthetic
|
||||
@JvmDefault
|
||||
public override suspend fun sendMessage(message: String): Unit =
|
||||
withContext(Dispatchers.IO) { sendMessageJ(message) }
|
||||
}
|
||||
|
||||
public val consoleCommandSender: ConsoleCommandSenderImpl
|
||||
|
||||
public val dataStorageForJarPluginLoader: PluginDataStorage
|
||||
public val configStorageForJarPluginLoader: PluginDataStorage
|
||||
@ -91,7 +133,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
/**
|
||||
* 创建一个 [LoginSolver]
|
||||
*
|
||||
* **调用备注**: 此函数通常在构造 [Bot] 实例时调用.
|
||||
* **备注**: 此函数通常在构造 [Bot] 实例, 即 [MiraiConsole.addBot] 时调用.
|
||||
*
|
||||
* @param requesterBot 请求者 [Bot.id]
|
||||
* @param configuration 请求者 [Bot.configuration]
|
||||
@ -101,15 +143,18 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
public fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver
|
||||
|
||||
/**
|
||||
* 创建一个 logger
|
||||
* 创建一个 [MiraiLogger].
|
||||
*
|
||||
* **注意**: [MiraiConsole] 会将 [net.mamoe.mirai.utils.DefaultLogger] 设置为 `MiraiConsole::createLogger`.
|
||||
* 因此不要在 [createLogger] 中调用 [net.mamoe.mirai.utils.DefaultLogger]
|
||||
*/
|
||||
public fun newLogger(identity: String?): MiraiLogger
|
||||
public fun createLogger(identity: String?): MiraiLogger
|
||||
|
||||
public companion object {
|
||||
internal lateinit var instance: MiraiConsoleImplementation
|
||||
private val initLock = ReentrantLock()
|
||||
|
||||
/** 由前端调用, 初始化 [MiraiConsole] 实例, 并启动 */
|
||||
/** 由前端调用, 初始化 [MiraiConsole] 实例并启动 */
|
||||
@JvmStatic
|
||||
@ConsoleFrontEndImplementation
|
||||
@Throws(MalformedMiraiConsoleImplementationError::class)
|
||||
|
@ -153,7 +153,7 @@ public object BuiltInCommands {
|
||||
onFailure = { throwable ->
|
||||
sendMessage(
|
||||
"Login failed: ${throwable.localizedMessage ?: throwable.message ?: throwable.toString()}" +
|
||||
if (this is MessageEventContextAware<*>) {
|
||||
if (this is CommandSenderOnMessage<*>) {
|
||||
CommandManagerImpl.launch(CoroutineName("stacktrace delayer from Login")) {
|
||||
fromEvent.nextMessageOrNull(60.secondsToMillis) { it.message.contentEquals("stacktrace") }
|
||||
}
|
||||
|
@ -7,90 +7,275 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "INAPPLICABLE_JVM_NAME")
|
||||
@file:Suppress(
|
||||
"NOTHING_TO_INLINE", "INAPPLICABLE_JVM_NAME", "FunctionName", "SuspendFunctionOnCoroutineScope",
|
||||
"unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER"
|
||||
)
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.asCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.asMemberCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.asTempCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender.INSTANCE
|
||||
import net.mamoe.mirai.console.command.description.CommandArgumentParserException
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.childScope
|
||||
import net.mamoe.mirai.console.util.childScopeContext
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.message.*
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
|
||||
/**
|
||||
* 指令发送者.
|
||||
*
|
||||
* ### 获得指令发送者
|
||||
* 只有 [CommandSender] 才能 [执行指令][CommandManager.execute]
|
||||
*
|
||||
* ## 获得指令发送者
|
||||
* - [MessageEvent.toCommandSender]
|
||||
* - [FriendMessageEvent.toCommandSender]
|
||||
* - [GroupMessageEvent.toCommandSender]
|
||||
* - [TempMessageEvent.toCommandSender]
|
||||
*
|
||||
* - [Member.asCommandSender]
|
||||
* - [Member.asTempCommandSender]]
|
||||
* - [Member.asMemberCommandSender]]
|
||||
* - [Friend.asCommandSender]
|
||||
* - [User.asCommandSender]
|
||||
*
|
||||
* ### 子类型
|
||||
* ## 实现 [CommandSender]
|
||||
* 除 Console 前端外, 在任何时候都不要实现 [CommandSender] (包括使用委托). 必须使用上述扩展获取 [CommandSender] 实例.
|
||||
*
|
||||
* 当真实收到由用户执行的指令时:
|
||||
* - 若用户在群内指令执行, 对应 [CommandSender] 为 [MemberCommandSenderOnMessage]
|
||||
* - 若用户在私聊环境内指令执行, 对应 [CommandSender] 为 [FriendCommandSenderOnMessage]
|
||||
* - 若用户在临时会话内指令执行, 对应 [CommandSender] 为 [TempCommandSenderOnMessage]
|
||||
* Console 前端可实现 [ConsoleCommandSender]
|
||||
*
|
||||
* 当指令由其他插件主动执行时, 插件应使用 [toCommandSender] 或 [asCommandSender], 因此
|
||||
* - 若用户在群内指令执行, 对应 [CommandSender] 为 [MemberCommandSender]
|
||||
* - 若用户在私聊环境内指令执行, 对应 [CommandSender] 为 [FriendCommandSender]
|
||||
* - 若用户在临时会话内指令执行, 对应 [CommandSender] 为 [TempCommandSender]
|
||||
* ## 子类型
|
||||
*
|
||||
* 所有 [CommandSender] 都应继承 [AbstractCommandSender].
|
||||
*
|
||||
* [AbstractCommandSender] 是密封类, 一级子类为:
|
||||
* - [AbstractUserCommandSender] 代表用户
|
||||
* - [ConsoleCommandSender] 代表控制台
|
||||
*
|
||||
* 二级子类, 当指令由插件 [主动执行][CommandManager.execute] 时, 插件应使用 [toCommandSender] 或 [asCommandSender], 因此,
|
||||
* - 若在群聊环境, 对应 [CommandSender] 为 [MemberCommandSender]
|
||||
* - 若在私聊环境, 对应 [CommandSender] 为 [FriendCommandSender]
|
||||
* - 若在临时会话环境, 对应 [CommandSender] 为 [TempCommandSender]
|
||||
*
|
||||
* 三级子类, 当真实收到由用户执行的指令时:
|
||||
* - 若在群聊环境, 对应 [CommandSender] 为 [MemberCommandSenderOnMessage]
|
||||
* - 若在私聊环境, 对应 [CommandSender] 为 [FriendCommandSenderOnMessage]
|
||||
* - 若在临时会话环境, 对应 [CommandSender] 为 [TempCommandSenderOnMessage]
|
||||
*
|
||||
* 类型关系如图. 箭头指向的是父类.
|
||||
*
|
||||
* ```
|
||||
* CoroutineScope
|
||||
* ↑
|
||||
* |
|
||||
* CommandSender <---------+---------------+-------------------------------+
|
||||
* ↑ | | |
|
||||
* | | | |
|
||||
* | UserCommandSender GroupAwareCommandSender CommandSenderOnMessage
|
||||
* | ↑ ↑ ↑
|
||||
* | | | |
|
||||
* AbstractCommandSender | | |
|
||||
* ↑ | | |
|
||||
* | sealed | | |
|
||||
* +-------------+-------------+ | | |
|
||||
* | | | | |
|
||||
* | | | | | }
|
||||
* ConsoleCommandSender AbstractUserCommandSender | | } 一级子类
|
||||
* ↑ | | }
|
||||
* | sealed | |
|
||||
* | | |
|
||||
* +----------------------+ | |
|
||||
* | | | |
|
||||
* | +------+------------+---------------+ |
|
||||
* | | | |
|
||||
* | | | | }
|
||||
* FriendCommandSender MemberCommandSender TempCommandSender | } 二级子类
|
||||
* ↑ ↑ ↑ | }
|
||||
* | | | |
|
||||
* | | | | }
|
||||
* FriendCommandSenderOnMessage MemberCommandSenderOnMessage TempCommandSenderOnMessage | } 三级子类
|
||||
* | | | | }
|
||||
* | | | |
|
||||
* +-----------------------------+----------------------------+---------------+
|
||||
* ```
|
||||
*
|
||||
* @see ConsoleCommandSender 控制台
|
||||
* @see UserCommandSender [User] ([群成员][Member], [好友][Friend])
|
||||
* @see toCommandSender
|
||||
* @see asCommandSender
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public interface CommandSender {
|
||||
public interface CommandSender : CoroutineScope {
|
||||
/**
|
||||
* 与这个 [CommandSender] 相关的 [Bot]. 当通过控制台执行时为 null.
|
||||
* 与这个 [CommandSender] 相关的 [Bot].
|
||||
* 当通过控制台执行时为 `null`.
|
||||
*/
|
||||
public val bot: Bot?
|
||||
|
||||
/**
|
||||
* 获取好友昵称, 群员昵称, 或 Bot 的昵称. 当控制台发送消息时返回 [ConsoleCommandSender.NAME]
|
||||
* 与这个 [CommandSender] 相关的 [Contact].
|
||||
*
|
||||
* - 当一个群员执行指令时, [subject] 为所在 [群][Group]
|
||||
* - 当通过控制台执行时为 `null`.
|
||||
*/
|
||||
public val subject: Contact?
|
||||
|
||||
/**
|
||||
* 指令原始发送*人*.
|
||||
* - 当通过控制台执行时为 `null`.
|
||||
*/
|
||||
public val user: User?
|
||||
|
||||
/**
|
||||
* [User.nameCardOrNick] 或 [ConsoleCommandSender.NAME]
|
||||
*/
|
||||
public val name: String
|
||||
|
||||
/**
|
||||
* 立刻发送一条消息. 对于 [Member.asCommandSender], 这个函数总是发送给所在群
|
||||
* 立刻发送一条消息.
|
||||
*
|
||||
* 对于 [MemberCommandSender], 这个函数总是发送给所在群
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendMessage(message: Message)
|
||||
public suspend fun sendMessage(message: Message): MessageReceipt<User>?
|
||||
|
||||
/**
|
||||
* 立刻发送一条消息. 对于 [Member.asCommandSender], 这个函数总是发送给所在群
|
||||
* 立刻发送一条消息.
|
||||
*
|
||||
* 对于 [MemberCommandSender], 这个函数总是发送给所在群
|
||||
*/
|
||||
@JvmDefault
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendMessage(message: String): Unit = sendMessage(PlainText(message))
|
||||
public suspend fun sendMessage(message: String): MessageReceipt<User>?
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public suspend fun catchExecutionException(e: Throwable) {
|
||||
@ConsoleExperimentalAPI("This is unstable and might get changed")
|
||||
public suspend fun catchExecutionException(e: Throwable)
|
||||
|
||||
public companion object {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constructors
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 构造 [FriendCommandSenderOnMessage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun FriendMessageEvent.toCommandSender(): FriendCommandSenderOnMessage =
|
||||
FriendCommandSenderOnMessage(this)
|
||||
|
||||
/**
|
||||
* 构造 [MemberCommandSenderOnMessage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun GroupMessageEvent.toCommandSender(): MemberCommandSenderOnMessage =
|
||||
MemberCommandSenderOnMessage(this)
|
||||
|
||||
/**
|
||||
* 构造 [TempCommandSenderOnMessage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun TempMessageEvent.toCommandSender(): TempCommandSenderOnMessage = TempCommandSenderOnMessage(this)
|
||||
|
||||
/**
|
||||
* 构造 [CommandSenderOnMessage]
|
||||
*/
|
||||
@JvmStatic
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public fun <T : MessageEvent> T.toCommandSender(): CommandSenderOnMessage<T> = when (this) {
|
||||
is FriendMessageEvent -> toCommandSender()
|
||||
is GroupMessageEvent -> toCommandSender()
|
||||
is TempMessageEvent -> toCommandSender()
|
||||
else -> throw IllegalArgumentException("unsupported MessageEvent: ${this::class.qualifiedNameOrTip}")
|
||||
} as CommandSenderOnMessage<T>
|
||||
|
||||
/**
|
||||
* 得到 [TempCommandSender]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun Member.asTempCommandSender(): TempCommandSender = TempCommandSender(this)
|
||||
|
||||
/**
|
||||
* 得到 [MemberCommandSender]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun Member.asMemberCommandSender(): MemberCommandSender = MemberCommandSender(this)
|
||||
|
||||
/**
|
||||
* 得到 [MemberCommandSender] 或 [TempCommandSender]
|
||||
* @see asTempCommandSender
|
||||
* @see asMemberCommandSender
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun Member.asCommandSender(isTemp: Boolean): UserCommandSender {
|
||||
return if (isTemp) asTempCommandSender() else asMemberCommandSender()
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到 [FriendCommandSender]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this)
|
||||
|
||||
/**
|
||||
* 得到 [UserCommandSender]
|
||||
*
|
||||
* @param isTemp
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun User.asCommandSender(isTemp: Boolean): UserCommandSender = when (this) {
|
||||
is Friend -> this.asCommandSender()
|
||||
is Member -> if (isTemp) TempCommandSender(this) else MemberCommandSender(this)
|
||||
else -> error("stub")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有 [CommandSender] 都必须继承自此对象.
|
||||
* @see CommandSender 查看更多信息
|
||||
*/
|
||||
public sealed class AbstractCommandSender : CommandSender, CoroutineScope {
|
||||
public abstract override val bot: Bot?
|
||||
public abstract override val subject: Contact?
|
||||
public abstract override val user: User?
|
||||
public abstract override suspend fun sendMessage(message: Message): MessageReceipt<User>?
|
||||
public abstract override fun toString(): String
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<User>? = sendMessage(PlainText(message))
|
||||
|
||||
@ConsoleExperimentalAPI("This is unstable and might get changed")
|
||||
override suspend fun catchExecutionException(e: Throwable) {
|
||||
if (this is CommandSenderOnMessage<*>) {
|
||||
// TODO: 2020/8/22 bad scope
|
||||
val cause = e.rootCauseOrSelf
|
||||
|
||||
val message = cause
|
||||
.takeIf { it is CommandArgumentParserException }?.message
|
||||
?: "${cause::class.simpleName.orEmpty()}: ${cause.message}"
|
||||
|
||||
// TODO: 2020/8/30 优化 net.mamoe.mirai.console.command.CommandSender.catchExecutionException
|
||||
|
||||
sendMessage(message) // \n\n60 秒内发送 stacktrace 查看堆栈信息
|
||||
bot.launch(CoroutineName("stacktrace delayer from command")) {
|
||||
this@AbstractCommandSender.launch(CoroutineName("stacktrace delayer from command")) {
|
||||
if (fromEvent.nextMessageOrNull(60_000) {
|
||||
it.message.contentEquals("stacktrace") || it.message.contentEquals("stack")
|
||||
} != null) {
|
||||
@ -103,20 +288,199 @@ public interface CommandSender {
|
||||
}
|
||||
}
|
||||
|
||||
internal val Throwable.rootCauseOrSelf: Throwable get() = generateSequence(this) { it.cause }.lastOrNull() ?: this
|
||||
|
||||
/**
|
||||
* 可以知道其 [Bot] 的 [CommandSender]
|
||||
* 当 [this] 为 [AbstractCommandSender] 时返回.
|
||||
*
|
||||
* 正常情况下, 所有 [CommandSender] 都应该继承 [AbstractCommandSender]
|
||||
*
|
||||
* 在需要类型智能转换等情况时可使用此函数.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若函数正常返回, Kotlin 编译器认为 [this] 是 [AbstractCommandSender] 实例并执行智能类型转换.
|
||||
*
|
||||
* @return `this`
|
||||
*/
|
||||
public interface BotAwareCommandSender : CommandSender {
|
||||
public override val bot: Bot
|
||||
public fun CommandSender.checkIsAbstractCommandSender(): AbstractCommandSender {
|
||||
contract {
|
||||
returns() implies (this@checkIsAbstractCommandSender is AbstractCommandSender)
|
||||
}
|
||||
check(this is AbstractCommandSender) { "A CommandSender must extend AbstractCommandSender" }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以知道其 [Group] 环境的 [CommandSender]
|
||||
* 当 [this] 为 [AbstractUserCommandSender] 时返回.
|
||||
*
|
||||
* 正常情况下, 所有 [UserCommandSender] 都应该继承 [AbstractUserCommandSender]
|
||||
*
|
||||
* 在需要类型智能转换等情况时可使用此函数.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若函数正常返回, Kotlin 编译器认为 [this] 是 [AbstractUserCommandSender] 实例并执行智能类型转换.
|
||||
*
|
||||
* @return `this`
|
||||
*/
|
||||
public interface GroupAwareCommandSender : CommandSender {
|
||||
public val group: Group
|
||||
public fun UserCommandSender.checkIsAbstractUserCommandSender(): AbstractUserCommandSender {
|
||||
contract {
|
||||
returns() implies (this@checkIsAbstractUserCommandSender is AbstractUserCommandSender)
|
||||
}
|
||||
check(this is AbstractUserCommandSender) { "A UserCommandSender must extend AbstractUserCommandSender" }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [ConsoleCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isConsole(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isConsole is ConsoleCommandSender)
|
||||
returns(false) implies (this@isConsole is UserCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this is ConsoleCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 不为 [ConsoleCommandSender], 即为 [UserCommandSender] 时返回 `true`.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isNotConsole(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isNotConsole is UserCommandSender)
|
||||
returns(false) implies (this@isNotConsole is ConsoleCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this !is ConsoleCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [UserCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isUser(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isUser is UserCommandSender)
|
||||
returns(false) implies (this@isUser is ConsoleCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this is UserCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 不为 [UserCommandSender], 即为 [ConsoleCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isNotUser(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isNotUser is ConsoleCommandSender)
|
||||
returns(false) implies (this@isNotUser is UserCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this !is UserCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 折叠 [AbstractCommandSender] 的两种可能性.
|
||||
*
|
||||
* - 当 [this] 为 [ConsoleCommandSender] 时执行 [ifIsConsole]
|
||||
* - 当 [this] 为 [UserCommandSender] 时执行 [ifIsUser]
|
||||
*
|
||||
* ### 示例
|
||||
* ```
|
||||
* // 当一个指令执行过程出错
|
||||
* val exception: Exception = ...
|
||||
*
|
||||
* sender.fold(
|
||||
* ifIsConsole = { // this: ConsoleCommandSender
|
||||
* sendMessage(exception.stackTraceToString()) // 展示整个 stacktrace
|
||||
* },
|
||||
* ifIsUser = { // this: UserCommandSender
|
||||
* sendMessage(exception.message.toString()) // 只展示 Exception.message
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @return [ifIsConsole] 或 [ifIsUser] 执行结果.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@LowPriorityInOverloadResolution
|
||||
public inline fun <R> CommandSender.fold(
|
||||
ifIsConsole: ConsoleCommandSender.() -> R,
|
||||
ifIsUser: UserCommandSender.() -> R,
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(ifIsConsole, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(ifIsUser, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
return when (val sender = this.checkIsAbstractCommandSender()) {
|
||||
is ConsoleCommandSender -> ifIsConsole(sender)
|
||||
is AbstractUserCommandSender -> ifIsUser(sender)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 折叠 [AbstractCommandSender] 的两种可能性, 即在群内发送或在私聊环境发送.
|
||||
*
|
||||
* - 当 [this] 为 [MemberCommandSender] 时执行 [inGroup]
|
||||
* - 当 [this] 为 [TempCommandSender] 或 [FriendCommandSender] 时执行 [inPrivate]
|
||||
*
|
||||
* ### 实验性 API
|
||||
*
|
||||
* 这是预览版本 API. 如果你对 [UserCommandSender.fold] 有建议, 请在 [mamoe/mirai-console/issues](https://github.com/mamoe/mirai-console/issues/new) 提交.
|
||||
*
|
||||
* @return [inGroup] 或 [inPrivate] 执行结果.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@ConsoleExperimentalAPI
|
||||
public inline fun <R> UserCommandSender.foldContext(
|
||||
inGroup: MemberCommandSender.() -> R,
|
||||
inPrivate: UserCommandSender.() -> R,
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(inGroup, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(inPrivate, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
return when (val sender = this.checkIsAbstractUserCommandSender()) {
|
||||
is MemberCommandSender -> inGroup(sender)
|
||||
else -> inPrivate(sender)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取 [Group].
|
||||
*
|
||||
* 当 [GroupAwareCommandSender] 时返回 [GroupAwareCommandSender.group], 否则返回 `null`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回非 `null` 实例, Kotlin 编译器认为 [this] 是 [GroupAwareCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `null`, Kotlin 编译器认为 [this] 是 [FriendCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.getBotOrNull(): Bot? {
|
||||
contract {
|
||||
returns(null) implies (this@getBotOrNull is AbstractUserCommandSender)
|
||||
returnsNotNull() implies (this@getBotOrNull is ConsoleCommandSender)
|
||||
}
|
||||
return this.castOrNull<UserCommandSender>()?.bot
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,154 +488,190 @@ public interface GroupAwareCommandSender : CommandSender {
|
||||
* @see INSTANCE
|
||||
*/
|
||||
// 前端实现
|
||||
public abstract class ConsoleCommandSender internal constructor() : CommandSender {
|
||||
public abstract class ConsoleCommandSender @ConsoleFrontEndImplementation constructor() : AbstractCommandSender() {
|
||||
public final override val bot: Nothing? get() = null
|
||||
public override val name: String get() = NAME
|
||||
public final override val subject: Nothing? get() = null
|
||||
public final override val user: Nothing? get() = null
|
||||
public final override val name: String get() = NAME
|
||||
public final override fun toString(): String = NAME
|
||||
|
||||
public companion object {
|
||||
public const val NAME: String = "CONSOLE"
|
||||
|
||||
internal val instance get() = MiraiConsoleImplementationBridge.consoleCommandSender
|
||||
}
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun FriendMessageEvent.toCommandSender(): FriendCommandSenderOnMessage = FriendCommandSenderOnMessage(this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun GroupMessageEvent.toCommandSender(): MemberCommandSenderOnMessage = MemberCommandSenderOnMessage(this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun TempMessageEvent.toCommandSender(): TempCommandSenderOnMessage = TempCommandSenderOnMessage(this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun MessageEvent.toCommandSender(): CommandSenderOnMessage<*> = when (this) {
|
||||
is FriendMessageEvent -> toCommandSender()
|
||||
is GroupMessageEvent -> toCommandSender()
|
||||
is TempMessageEvent -> toCommandSender()
|
||||
else -> throw IllegalArgumentException("unsupported MessageEvent: ${this::class.qualifiedNameOrTip}")
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun Member.asCommandSender(): MemberCommandSender = MemberCommandSender(this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun Friend.asCommandSender(): FriendCommandSender = FriendCommandSender(this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public fun User.asCommandSender(): UserCommandSender {
|
||||
return when (this) {
|
||||
is Friend -> this.asCommandSender()
|
||||
is Member -> this.asCommandSender()
|
||||
else -> error("stub")
|
||||
public companion object INSTANCE : ConsoleCommandSender(), CoroutineScope {
|
||||
public const val NAME: String = "ConsoleCommandSender"
|
||||
public override val coroutineContext: CoroutineContext by lazy { MiraiConsole.childScopeContext(NAME) }
|
||||
public override suspend fun sendMessage(message: Message): Nothing? {
|
||||
MiraiConsoleImplementationBridge.consoleCommandSender.sendMessage(message)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示由 [MessageEvent] 触发的指令
|
||||
* 知道 [Group] 环境的 [UserCommandSender]
|
||||
*
|
||||
* 可能的子类:
|
||||
*
|
||||
* - [MemberCommandSender] 代表一个 [群员][Member] 执行指令
|
||||
* - [TempCommandSender] 代表一个 [群员][Member] 通过临时会话执行指令
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface MessageEventContextAware<E : MessageEvent> : MessageEventExtensions<User, Contact> {
|
||||
public val fromEvent: E
|
||||
public interface GroupAwareCommandSender : UserCommandSender {
|
||||
public val group: Group
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个用户私聊机器人执行指令
|
||||
* @see User.asCommandSender
|
||||
* 尝试获取 [Group].
|
||||
*
|
||||
* 当 [GroupAwareCommandSender] 时返回 [GroupAwareCommandSender.group], 否则返回 `null`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回非 `null` 实例, Kotlin 编译器认为 [this] 是 [GroupAwareCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `null`, Kotlin 编译器认为 [this] 是 [FriendCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public sealed class UserCommandSender : CommandSender, BotAwareCommandSender {
|
||||
public fun CommandSender.getGroupOrNull(): Group? {
|
||||
contract {
|
||||
returns(null) implies (this@getGroupOrNull is FriendCommandSender)
|
||||
returnsNotNull() implies (this@getGroupOrNull is GroupAwareCommandSender)
|
||||
}
|
||||
return this.castOrNull<GroupAwareCommandSender>()?.group
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// UserCommandSender
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 代表一个用户执行指令
|
||||
*
|
||||
* @see MemberCommandSender 代表一个 [群员][Member] 执行指令
|
||||
* @see FriendCommandSender 代表一个 [好友][Friend] 执行指令
|
||||
* @see TempCommandSender 代表一个 [群员][Member] 通过临时会话执行指令
|
||||
*
|
||||
* @see CommandSenderOnMessage
|
||||
*/
|
||||
public interface UserCommandSender : CommandSender {
|
||||
/**
|
||||
* @see MessageEvent.sender
|
||||
*/
|
||||
public abstract val user: User
|
||||
public override val user: User // override nullability
|
||||
|
||||
/**
|
||||
* @see MessageEvent.subject
|
||||
*/
|
||||
public abstract val subject: Contact
|
||||
|
||||
public override val bot: Bot get() = user.bot
|
||||
public override val name: String get() = user.nameCardOrNick
|
||||
|
||||
public final override suspend fun sendMessage(message: Message) {
|
||||
subject.sendMessage(message)
|
||||
}
|
||||
public override val subject: Contact // override nullability
|
||||
public override val bot: Bot // override nullability
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个用户私聊机器人执行指令
|
||||
* @see Friend.asCommandSender
|
||||
* [UserCommandSender] 的实现
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public open class FriendCommandSender(
|
||||
public sealed class AbstractUserCommandSender : UserCommandSender, AbstractCommandSender() {
|
||||
public override val bot: Bot get() = user.bot // don't final
|
||||
public final override val name: String get() = user.nameCardOrNick
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<User> = sendMessage(PlainText(message))
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<User> = user.sendMessage(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个 [好友][Friend] 执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* @see FriendCommandSenderOnMessage 代表一个真实的 [好友][Friend] 主动在私聊消息执行指令
|
||||
*/
|
||||
public open class FriendCommandSender internal constructor(
|
||||
public final override val user: Friend
|
||||
) : UserCommandSender() {
|
||||
) : AbstractUserCommandSender(), CoroutineScope by user.childScope("FriendCommandSender") {
|
||||
public override val subject: Contact get() = user
|
||||
public override fun toString(): String = "FriendCommandSender($user)"
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Friend> = sendMessage(PlainText(message))
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<Friend> = user.sendMessage(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个用户私聊机器人执行指令
|
||||
* @see Friend.asCommandSender
|
||||
* 代表一个 [群员][Member] 执行指令, 但不一定是通过群内发消息方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* @see MemberCommandSenderOnMessage 代表一个真实的 [群员][Member] 主动在群内发送消息执行指令.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public class FriendCommandSenderOnMessage(
|
||||
public open class MemberCommandSender internal constructor(
|
||||
public final override val user: Member
|
||||
) : AbstractUserCommandSender(),
|
||||
GroupAwareCommandSender,
|
||||
CoroutineScope by user.childScope("MemberCommandSender") {
|
||||
public override val group: Group get() = user.group
|
||||
public override val subject: Contact get() = group
|
||||
public override fun toString(): String = "MemberCommandSender($user)"
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Member> = sendMessage(PlainText(message))
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<Member> = user.sendMessage(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个 [群员][Member] 通过临时会话执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* @see TempCommandSenderOnMessage 代表一个 [群员][Member] 主动在临时会话发送消息执行指令
|
||||
*/
|
||||
public open class TempCommandSender internal constructor(
|
||||
public final override val user: Member
|
||||
) : AbstractUserCommandSender(),
|
||||
GroupAwareCommandSender,
|
||||
CoroutineScope by user.childScope("TempCommandSender") {
|
||||
public override val group: Group get() = user.group
|
||||
public override val subject: Contact get() = group
|
||||
public override fun toString(): String = "TempCommandSender($user)"
|
||||
public override suspend fun sendMessage(message: String): MessageReceipt<Member> = sendMessage(PlainText(message))
|
||||
public override suspend fun sendMessage(message: Message): MessageReceipt<Member> = user.sendMessage(message)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// CommandSenderOnMessage
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 代表一个真实 [用户][User] 主动私聊机器人或在群内发送消息而执行指令
|
||||
*
|
||||
* @see MemberCommandSenderOnMessage 代表一个真实的 [群员][Member] 主动在群内发送消息执行指令.
|
||||
* @see FriendCommandSenderOnMessage 代表一个真实的 [好友][Friend] 主动在私聊消息执行指令
|
||||
* @see TempCommandSenderOnMessage 代表一个 [群员][Member] 主动在临时会话发送消息执行指令
|
||||
*/
|
||||
public interface CommandSenderOnMessage<T : MessageEvent> :
|
||||
CommandSender,
|
||||
MessageEventExtensions<User, Contact> {
|
||||
|
||||
/**
|
||||
* 消息源 [MessageEvent]
|
||||
*/
|
||||
public val fromEvent: T
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个真实的 [好友][Friend] 主动在私聊消息执行指令
|
||||
* @see FriendCommandSender 代表一个 [好友][Friend] 执行指令, 但不一定是通过私聊方式
|
||||
*/
|
||||
public class FriendCommandSenderOnMessage internal constructor(
|
||||
public override val fromEvent: FriendMessageEvent
|
||||
) : FriendCommandSender(fromEvent.sender),
|
||||
CommandSenderOnMessage<FriendMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Contact get() = super.subject
|
||||
CommandSenderOnMessage<FriendMessageEvent>,
|
||||
MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Friend get() = fromEvent.subject
|
||||
public override val bot: Bot get() = super.bot
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public interface CommandSenderOnMessage<T : MessageEvent> : MessageEventContextAware<T>, CommandSender
|
||||
|
||||
/**
|
||||
* 代表一个群成员执行指令.
|
||||
* @see Member.asCommandSender
|
||||
* 代表一个真实的 [群员][Member] 主动在群内发送消息执行指令.
|
||||
* @see MemberCommandSender 代表一个 [群员][Member] 执行指令, 但不一定是通过群内发消息方式
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public open class MemberCommandSender(
|
||||
public final override val user: Member
|
||||
) : UserCommandSender(), GroupAwareCommandSender {
|
||||
public override val group: Group get() = user.group
|
||||
public override val subject: Contact get() = group
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个群成员执行指令.
|
||||
* @see Member.asCommandSender
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public open class TempCommandSender(
|
||||
public final override val user: Member
|
||||
) : UserCommandSender(), GroupAwareCommandSender {
|
||||
public override val group: Group get() = user.group
|
||||
public override val subject: Contact get() = group
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个群成员在群内执行指令.
|
||||
* @see Member.asCommandSender
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public class MemberCommandSenderOnMessage(
|
||||
public class MemberCommandSenderOnMessage internal constructor(
|
||||
public override val fromEvent: GroupMessageEvent
|
||||
) : MemberCommandSender(fromEvent.sender),
|
||||
CommandSenderOnMessage<GroupMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Contact get() = super.subject
|
||||
CommandSenderOnMessage<GroupMessageEvent>,
|
||||
MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Group get() = fromEvent.subject
|
||||
public override val bot: Bot get() = super.bot
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个群成员通过临时会话私聊机器人执行指令.
|
||||
* @see Member.asCommandSender
|
||||
* 代表一个 [群员][Member] 主动在临时会话发送消息执行指令
|
||||
* @see TempCommandSender 代表一个 [群员][Member] 通过临时会话执行指令, 但不一定是通过私聊方式
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public class TempCommandSenderOnMessage(
|
||||
public class TempCommandSenderOnMessage internal constructor(
|
||||
public override val fromEvent: TempMessageEvent
|
||||
) : TempCommandSender(fromEvent.sender),
|
||||
CommandSenderOnMessage<TempMessageEvent>, MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Contact get() = super.subject
|
||||
CommandSenderOnMessage<TempMessageEvent>,
|
||||
MessageEventExtensions<User, Contact> by fromEvent {
|
||||
public override val subject: Member get() = fromEvent.subject
|
||||
public override val bot: Bot get() = super.bot
|
||||
}
|
@ -81,7 +81,9 @@ public abstract class SimpleCommand(
|
||||
}
|
||||
|
||||
@Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
|
||||
internal override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) = sendMessage(usage)
|
||||
internal override suspend fun CommandSender.onDefault(rawArgs: Array<out Any>) {
|
||||
sendMessage(usage)
|
||||
}
|
||||
|
||||
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
|
||||
get() = SimpleCommandSubCommandAnnotationResolver
|
||||
|
@ -308,7 +308,7 @@ internal interface InternalCommandArgumentParserExtensions<T : Any> : CommandArg
|
||||
}
|
||||
|
||||
fun CommandSender.inferBotOrFail(): Bot =
|
||||
(this as? BotAwareCommandSender)?.bot
|
||||
(this as? UserCommandSender)?.bot
|
||||
?: Bot.botInstancesSequence.singleOrNull()
|
||||
?: illegalArgument("当前语境下无法推断目标 Bot, 因为目前有多个 Bot 在线.")
|
||||
|
||||
|
@ -22,7 +22,6 @@ import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.data.builtin.ConsoleDataScope
|
||||
@ -33,7 +32,6 @@ import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
@ -54,11 +52,12 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
override val rootPath: Path by instance::rootPath
|
||||
override val frontEndDescription: MiraiConsoleFrontEndDescription by instance::frontEndDescription
|
||||
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
override val mainLogger: MiraiLogger by instance::mainLogger
|
||||
override val mainLogger: MiraiLogger by lazy {
|
||||
createLogger("main")
|
||||
}
|
||||
override val coroutineContext: CoroutineContext by instance::coroutineContext
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> by instance::builtInPluginLoaders
|
||||
override val consoleCommandSender: ConsoleCommandSender by instance::consoleCommandSender
|
||||
override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl by instance::consoleCommandSender
|
||||
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage by instance::dataStorageForJarPluginLoader
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage by instance::configStorageForJarPluginLoader
|
||||
@ -70,11 +69,10 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
instance.createLoginSolver(requesterBot, configuration)
|
||||
|
||||
init {
|
||||
DefaultLogger = this::newLogger
|
||||
DefaultLogger = this::createLogger
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
override fun newLogger(identity: String?): MiraiLogger = instance.newLogger(identity)
|
||||
override fun createLogger(identity: String?): MiraiLogger = instance.createLogger(identity)
|
||||
|
||||
@OptIn(ConsoleExperimentalAPI::class)
|
||||
internal fun doStart() {
|
||||
|
@ -15,6 +15,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.*
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.subscribeAlways
|
||||
import net.mamoe.mirai.message.MessageEvent
|
||||
@ -27,7 +28,7 @@ import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
internal object CommandManagerImpl : CommandManager, CoroutineScope by CoroutineScope(MiraiConsole.job) {
|
||||
private val logger: MiraiLogger by lazy {
|
||||
MiraiConsole.newLogger("command")
|
||||
MiraiConsole.createLogger("command")
|
||||
}
|
||||
|
||||
@JvmField
|
||||
|
@ -159,6 +159,15 @@ internal inline fun <reified R> Any.cast(): R {
|
||||
return this as R
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <reified R> Any.castOrNull(): R? {
|
||||
contract {
|
||||
returnsNotNull() implies (this@castOrNull is R)
|
||||
}
|
||||
return this as? R
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <reified R> Any.castOrInternalError(): R {
|
||||
contract {
|
||||
|
@ -36,7 +36,7 @@ internal object JarPluginLoaderImpl :
|
||||
override val configStorage: PluginDataStorage
|
||||
get() = MiraiConsoleImplementationBridge.configStorageForJarPluginLoader
|
||||
|
||||
private val logger: MiraiLogger = MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
||||
private val logger: MiraiLogger = MiraiConsole.createLogger(JarPluginLoader::class.simpleName!!)
|
||||
|
||||
override val dataStorage: PluginDataStorage
|
||||
get() = MiraiConsoleImplementationBridge.dataStorageForJarPluginLoader
|
||||
|
@ -43,7 +43,7 @@ internal abstract class JvmPluginInternal(
|
||||
|
||||
// region JvmPlugin
|
||||
final override val logger: MiraiLogger by lazy {
|
||||
MiraiConsole.newLogger(
|
||||
MiraiConsole.createLogger(
|
||||
"Plugin ${this.description.name}"
|
||||
)
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
@Suppress("ObjectPropertyName")
|
||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||
private val logger = MiraiConsole.newLogger("plugin")
|
||||
private val logger = MiraiConsole.createLogger("plugin")
|
||||
|
||||
@JvmField
|
||||
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||
|
@ -16,7 +16,6 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.MemoryPluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.plugin.DeferredPluginLoader
|
||||
@ -26,7 +25,10 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.LoginSolver
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.PlatformLogger
|
||||
import java.nio.file.Path
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -49,11 +51,17 @@ fun initTestEnvironment() {
|
||||
get() = Semver("1.0.0")
|
||||
|
||||
}
|
||||
override val mainLogger: MiraiLogger = DefaultLogger("main")
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = listOf(DeferredPluginLoader { JarPluginLoader })
|
||||
override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() {
|
||||
override suspend fun sendMessage(message: Message) = println(message)
|
||||
}
|
||||
override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl =
|
||||
object : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
println(message)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage = MemoryPluginDataStorage()
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MemoryPluginDataStorage()
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MemoryPluginDataStorage()
|
||||
@ -69,7 +77,7 @@ fun initTestEnvironment() {
|
||||
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
|
||||
LoginSolver.Default
|
||||
|
||||
override fun newLogger(identity: String?): MiraiLogger {
|
||||
override fun createLogger(identity: String?): MiraiLogger {
|
||||
return PlatformLogger(identity)
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ object TestSimpleCommand : RawCommand(owner, "testSimple", "tsS") {
|
||||
}
|
||||
}
|
||||
|
||||
internal val sender by lazy { ConsoleCommandSender.instance }
|
||||
internal val sender by lazy { ConsoleCommandSender }
|
||||
internal val owner by lazy { ConsoleCommandOwner }
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.CommandExecuteStatus
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.console.util.requestInput
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
@ -41,7 +42,7 @@ internal fun startupConsoleThread() {
|
||||
continue
|
||||
}
|
||||
// consoleLogger.debug("INPUT> $next")
|
||||
val result = ConsoleCommandSenderImpl.executeCommand(next)
|
||||
val result = ConsoleCommandSender.executeCommand(next)
|
||||
when (result.status) {
|
||||
CommandExecuteStatus.SUCCESSFUL -> {
|
||||
}
|
||||
|
@ -27,10 +27,8 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
@ -69,22 +67,19 @@ class MiraiConsoleImplementationPure
|
||||
listOf(DeferredPluginLoader { JarPluginLoader })
|
||||
),
|
||||
override val frontEndDescription: MiraiConsoleFrontEndDescription = ConsoleFrontEndDescImpl,
|
||||
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
|
||||
override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl = ConsoleCommandSenderImplPure,
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
|
||||
override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config"))
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(NamedSupervisorJob("MiraiConsoleImplementationPure")) {
|
||||
override val mainLogger: MiraiLogger by lazy {
|
||||
MiraiConsole.newLogger("main")
|
||||
}
|
||||
override val consoleInput: ConsoleInput get() = ConsoleInputImpl
|
||||
|
||||
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {
|
||||
return DefaultLoginSolver(input = { requestInput("LOGIN> ") })
|
||||
}
|
||||
|
||||
override fun newLogger(identity: String?): MiraiLogger = LoggerCreator(identity)
|
||||
override fun createLogger(identity: String?): MiraiLogger = LoggerCreator(identity)
|
||||
|
||||
init {
|
||||
with(rootPath.toFile()) {
|
||||
|
@ -23,8 +23,8 @@ package net.mamoe.mirai.console.pure
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
@ -74,12 +74,16 @@ internal fun overrideSTD() {
|
||||
}
|
||||
|
||||
|
||||
internal object ConsoleCommandSenderImpl : ConsoleCommandSender() {
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
internal object ConsoleCommandSenderImplPure : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
||||
override suspend fun sendMessage(message: String) {
|
||||
kotlin.runCatching {
|
||||
lineReader.printAbove(message.contentToString())
|
||||
lineReader.printAbove(message)
|
||||
}.onFailure {
|
||||
consoleLogger.error(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
return sendMessage(message.contentToString())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user