Him188 6 rokov pred
rodič
commit
22175eaabb

+ 60 - 69
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt

@@ -2,6 +2,7 @@
 
 package net.mamoe.mirai.event
 
+import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.isAdministrator
 import net.mamoe.mirai.contact.isOperator
@@ -13,20 +14,19 @@ import net.mamoe.mirai.message.data.Message
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
-import kotlin.jvm.JvmName
 
 /**
  * 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
+inline fun CoroutineScope.subscribeMessages(crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
-        subscribeAlways<MessagePacket<*, *>> {
-            listener(it)
+        subscribeAlways {
+            listener(it, this.message.toString())
         }
     }.apply { listeners() }
 }
@@ -36,13 +36,13 @@ suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubsc
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun subscribeGroupMessages(crossinline listeners: suspend MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
+inline fun CoroutineScope.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<GroupMessage> { listener ->
-        subscribeAlways<GroupMessage> {
-            listener(it)
+        subscribeAlways {
+            listener(it, this.message.toString())
         }
     }.apply { listeners() }
 }
@@ -52,13 +52,13 @@ suspend inline fun subscribeGroupMessages(crossinline listeners: suspend Message
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun subscribeFriendMessages(crossinline listeners: suspend MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
+inline fun CoroutineScope.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<FriendMessage> { listener ->
-        subscribeAlways<FriendMessage> {
-            listener(it)
+        subscribeAlways {
+            listener(it, this.message.toString())
         }
     }.apply { listeners() }
 }
@@ -68,13 +68,13 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
+inline fun Bot.subscribeMessages(crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
-        this.subscribeAlways<MessagePacket<*, *>> {
-            listener(it)
+        this.subscribeAlways {
+            listener(it, this.message.toString())
         }
     }.apply { listeners() }
 }
@@ -84,13 +84,13 @@ suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageS
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
+inline fun Bot.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<GroupMessage> { listener ->
-        this.subscribeAlways<GroupMessage> {
-            listener(it)
+        this.subscribeAlways {
+            listener(it, this.message.toString())
         }
     }.apply { listeners() }
 }
@@ -100,45 +100,36 @@ suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend Mes
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun Bot.subscribeFriendMessages(crossinline listeners: suspend MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
+inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
     MessageSubscribersBuilder<FriendMessage> { listener ->
-        this.subscribeAlways<FriendMessage> {
-            listener(it)
+        this.subscribeAlways {
+            it.listener(it.message.toString())
         }
     }.apply { listeners() }
 }
 
-private typealias AnyReplier<T> = @MessageDsl suspend T.(String) -> Any?
-
-private suspend inline operator fun <T : MessagePacket<*, *>> (@MessageDsl suspend T.(String) -> Unit).invoke(t: T) =
-    this.invoke(t, t.message.stringValue)
-
-@JvmName("invoke1") //Avoid Platform declaration clash
-private suspend inline operator fun <T : MessagePacket<*, *>> AnyReplier<T>.invoke(t: T): Any? =
-    this.invoke(t, t.message.stringValue)
-
 /**
  * 消息订阅构造器
  *
  * @see subscribeFriendMessages
  * @sample demo.subscribe.messageDSL
  */
-// TODO: 2019/11/29 应定义为 inline, 但这会导致一个 JVM run-time VerifyError. 等待 kotlin 修复 bug
+// TODO: 2019/12/23 应定义为 inline, 但这会导致一个 JVM run-time VerifyError. 等待 kotlin 修复 bug (Kotlin 1.3.61)
 @Suppress("unused")
 @MessageDsl
 class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
-    inline val subscriber: suspend (@MessageDsl suspend T.(String) -> Unit) -> Unit
+    val subscriber: (@MessageDsl suspend T.(String) -> Unit) -> Listener<T>
 ) {
     /**
      * 无任何触发条件.
      */
     @MessageDsl
-    suspend inline fun always(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
-        content({ true }, onEvent)
-    } // TODO: 2019/12/4 这些 onEvent 都应该为 cross-inline, 而这会导致一个 CompilationException
+    inline fun always(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> {
+        return content({ true }, onEvent)
+    }
 
     /**
      * 如果消息内容 `==` [equals], 就执行 [onEvent]
@@ -146,97 +137,97 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param ignoreCase `true` 则不区分大小写
      */
     @MessageDsl
-    suspend inline fun case(
+    inline fun case(
         equals: String,
         trim: Boolean = true,
         ignoreCase: Boolean = false,
-        noinline onEvent: @MessageDsl suspend T.(String) -> Unit
-    ) {
+        crossinline onEvent: @MessageDsl suspend T.(String) -> Unit
+    ): Listener<T> {
         val toCheck = if (trim) equals.trim() else equals
-        content({ toCheck.equals(if (trim) it.trim() else it, ignoreCase = ignoreCase) }, onEvent)
+        return content({ toCheck.equals(if (trim) it.trim() else it, ignoreCase = ignoreCase) }, onEvent)
     }
 
     /**
      * 如果消息内容包含 [sub], 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun contains(sub: String, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) = content({ sub in it }, onEvent)
+    inline fun contains(sub: String, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> = content({ sub in it }, onEvent)
 
     /**
      * 如果消息的前缀是 [prefix], 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun startsWith(
+    inline fun startsWith(
         prefix: String,
         removePrefix: Boolean = true,
-        noinline onEvent: @MessageDsl suspend T.(String) -> Unit
-    ) =
+        crossinline onEvent: @MessageDsl suspend T.(String) -> Unit
+    ): Listener<T> =
         content({ it.startsWith(prefix) }) {
             if (removePrefix) this.onEvent(this.message.stringValue.substringAfter(prefix))
-            else onEvent(this)
+            else onEvent(this, this.message.toString())
         }
 
     /**
      * 如果消息的结尾是 [suffix], 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun endsWith(suffix: String, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun endsWith(suffix: String, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ it.endsWith(suffix) }, onEvent)
 
     /**
      * 如果是这个人发的消息, 就执行 [onEvent]. 消息可以是好友消息也可以是群消息
      */
     @MessageDsl
-    suspend inline fun sentBy(qqId: Long, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun sentBy(qqId: Long, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ sender.id == qqId }, onEvent)
 
     /**
      * 如果是管理员或群主发的消息, 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun sentByOperator(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun sentByOperator(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ this is GroupMessage && sender.permission.isOperator() }, onEvent)
 
     /**
      * 如果是管理员发的消息, 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun sentByAdministrator(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun sentByAdministrator(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ this is GroupMessage && sender.permission.isAdministrator() }, onEvent)
 
     /**
      * 如果是群主发的消息, 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun sentByOwner(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun sentByOwner(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ this is GroupMessage && sender.permission.isOwner() }, onEvent)
 
     /**
      * 如果是来自这个群的消息, 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun sentFrom(id: Long, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+    inline fun sentFrom(id: Long, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
         content({ if (this is GroupMessage) group.id == id else false }, onEvent)
 
     /**
      * 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent]
      */
     @MessageDsl
-    suspend inline fun <reified M : Message> has(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
-        subscriber { if (message.any { it::class == M::class }) onEvent(this) }
+    inline fun <reified M : Message> has(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
+        subscriber { if (message.any { it::class == M::class }) onEvent(this, this.message.toString()) }
 
     /**
      * 如果 [filter] 返回 `true` 就执行 `onEvent`
      */
     @MessageDsl
-    suspend inline fun content(noinline filter: T.(String) -> Boolean, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
-        subscriber { if (this.filter(message.stringValue)) onEvent(this) }
+    inline fun content(crossinline filter: T.(String) -> Boolean, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
+        subscriber { if (this.filter(message.toString())) onEvent(this, this.message.toString()) }
 
     /**
      * 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent`
      */
     @MessageDsl
-    suspend inline fun matching(regex: Regex, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
+    inline fun matching(regex: Regex, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
         content({ regex.matchEntire(it) != null }, onEvent)
     }
 
@@ -244,7 +235,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent`
      */
     @MessageDsl
-    suspend inline fun finding(regex: Regex, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
+    inline fun finding(regex: Regex, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
         content({ regex.find(it) != null }, onEvent)
     }
 
@@ -252,7 +243,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * 若消息内容包含 [this] 则回复 [reply]
      */
     @MessageDsl
-    suspend inline infix fun String.containsReply(reply: String) =
+    infix fun String.containsReply(reply: String) =
         content({ this@containsReply in it }) { [email protected](reply) }
 
     /**
@@ -263,7 +254,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
      */
     @MessageDsl
-    suspend inline infix fun String.containsReply(noinline replier: AnyReplier<T>) =
+    inline infix fun String.containsReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) =
         content({ this@containsReply in it }) {
             @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
             executeAndReply(replier)
@@ -277,7 +268,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
      */
     @MessageDsl
-    suspend inline infix fun Regex.matchingReply(noinline replier: AnyReplier<T>) {
+    inline infix fun Regex.matchingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
         content({ [email protected](it) != null }) {
             @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
             executeAndReply(replier)
@@ -292,7 +283,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
      */
     @MessageDsl
-    suspend inline infix fun Regex.findingReply(noinline replier: AnyReplier<T>) {
+    inline infix fun Regex.findingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
         content({ [email protected](it) != null }) {
             @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
             executeAndReply(replier)
@@ -313,7 +304,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
      */
     @MessageDsl
-    suspend inline infix fun String.startsWithReply(noinline replier: AnyReplier<T>) {
+    inline infix fun String.startsWithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
         val toCheck = this.trimStart()
         content({ it.trimStart().startsWith(toCheck) }) {
             @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
@@ -337,7 +328,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
      */
     @MessageDsl
-    suspend inline infix fun String.endswithReply(noinline replier: AnyReplier<T>) {
+    inline infix fun String.endswithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
         val toCheck = this.trimEnd()
         content({ it.endsWith(this@endswithReply) }) {
             @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
@@ -348,20 +339,20 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
     }
 
     @MessageDsl
-    suspend inline infix fun String.reply(reply: String) = case(this) {
+    infix fun String.reply(reply: String) = case(this) {
         [email protected](reply)
     }
 
     @MessageDsl
-    suspend inline infix fun String.reply(noinline replier: AnyReplier<T>) = case(this) {
+    inline infix fun String.reply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) = case(this) {
         @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
         executeAndReply(replier)
     }
 
     @PublishedApi
-    @Suppress("NOTHING_TO_INLINE")
-    internal suspend inline fun T.executeAndReply(noinline replier: AnyReplier<T>) {
-        when (val message = replier(this)) {
+    @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") // false positive
+    internal suspend inline fun T.executeAndReply(replier: @MessageDsl suspend T.(String) -> Any?) {
+        when (val message = replier(this, this.message.toString())) {
             is Message -> this.reply(message)
             is Unit -> {
 
@@ -371,10 +362,10 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
     }
 
 /* 易产生迷惑感
-    suspend inline fun replyCase(equals: String, trim: Boolean = true, noinline replier: MessageReplier<T>) = case(equals, trim) { reply(replier(this)) }
-    suspend inline fun replyContains(value: String, noinline replier: MessageReplier<T>) = content({ value in it }) { replier(this) }
-    suspend inline fun replyStartsWith(value: String, noinline replier: MessageReplier<T>) = content({ it.startsWith(value) }) { replier(this) }
-    suspend inline fun replyEndsWith(value: String, noinline replier: MessageReplier<T>) = content({ it.endsWith(value) }) { replier(this) }
+ fun replyCase(equals: String, trim: Boolean = true, replier: MessageReplier<T>) = case(equals, trim) { reply(replier(this)) }
+ fun replyContains(value: String, replier: MessageReplier<T>) = content({ value in it }) { replier(this) }
+ fun replyStartsWith(value: String, replier: MessageReplier<T>) = content({ it.startsWith(value) }) { replier(this) }
+ fun replyEndsWith(value: String, replier: MessageReplier<T>) = content({ it.endsWith(value) }) { replier(this) }
 */
 }
 

+ 3 - 30
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt

@@ -3,9 +3,6 @@
 package net.mamoe.mirai.event
 
 import net.mamoe.mirai.event.internal.broadcastInternal
-import net.mamoe.mirai.utils.DefaultLogger
-import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.withSwitch
 
 /**
  * 可被监听的.
@@ -13,9 +10,7 @@ import net.mamoe.mirai.utils.withSwitch
  * 可以是任何 class 或 object.
  *
  * @see subscribeAlways
- * @see subscribeOnce
  * @see subscribeWhile
- * @see subscribeAll
  *
  * @see subscribeMessages
  */
@@ -47,19 +42,6 @@ abstract class Event : Subscribable {
     fun cancel() {
         cancelled = true
     }
-
-    init {
-        if (EventDebuggingFlag) {
-            EventDebugLogger.debug(this::class.simpleName + " created")
-        }
-    }
-}
-
-internal object EventDebugLogger : MiraiLogger by DefaultLogger("Event").withSwitch(EventDebuggingFlag)
-
-private val EventDebuggingFlag: Boolean by lazy {
-    // avoid 'Condition is always true'
-    false
 }
 
 /**
@@ -74,19 +56,10 @@ interface Cancellable : Subscribable {
 /**
  * 广播一个事件的唯一途径.
  */
-@Suppress("UNCHECKED_CAST")
 suspend fun <E : Subscribable> E.broadcast(): E {
-    if (EventDebuggingFlag) {
-        EventDebugLogger.debug(this::class.simpleName + " pre broadcast")
-    }
-    try {
-        @Suppress("EXPERIMENTAL_API_USAGE")
-        return [email protected]()
-    } finally {
-        if (EventDebuggingFlag) {
-            EventDebugLogger.debug(this::class.simpleName + " after broadcast")
-        }
-    }
+    @Suppress("EXPERIMENTAL_API_USAGE")
+    [email protected]() // inline, no extra cost
+    return this
 }
 
 /**

+ 107 - 121
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt

@@ -1,12 +1,11 @@
-@file:Suppress("unused")
-
 package net.mamoe.mirai.event
 
+import kotlinx.coroutines.CompletableJob
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.GlobalScope
+import net.mamoe.mirai.Bot
 import net.mamoe.mirai.event.internal.Handler
-import net.mamoe.mirai.event.internal.Listener
 import net.mamoe.mirai.event.internal.subscribeInternal
-import kotlin.jvm.JvmStatic
-import kotlin.reflect.KClass
 
 /*
  * 该文件为所有的订阅事件的方法.
@@ -14,125 +13,112 @@ import kotlin.reflect.KClass
 
 /**
  * 订阅者的状态
- */ // Not using enum for Android
-inline class ListeningStatus(inline val listening: Boolean) {
-    companion object {
-        /**
-         * 表示继续监听
-         */
-        @JvmStatic
-        val LISTENING = ListeningStatus(true)
-
-        /**
-         * 表示已停止
-         */
-        @JvmStatic
-        val STOPPED = ListeningStatus(false)
-    }
+ */
+enum class ListeningStatus {
+    /**
+     * 表示继续监听
+     */
+    LISTENING,
+
+    /**
+     * 表示已停止
+     */
+    STOPPED
 }
 
+/**
+ * 事件监听器.
+ * 由 [subscribe] 等方法返回.
+ */
+interface Listener<in E : Subscribable> : CompletableJob {
+    suspend fun onEvent(event: E): ListeningStatus
+}
 
-// region 顶层方法
+// region 顶层方法 创建当前 coroutineContext 下的子 Job
 
 /**
- * 订阅所有 [E] 及其子类事件.
+ * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Subscribable.broadcast] 时, [handler] 都会被执行.
+ *
+ * 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听.
+ * 或 [Listener] complete 时结束.
+ *
+ *
+ * **注意**: 这个函数返回 [Listener], 它是一个 [CompletableJob]. 如果不手动 [CompletableJob.complete], 它将会阻止当前 [CoroutineScope] 结束.
+ * 例如:
+ * ```kotlin
+ * runBlocking { // this: CoroutineScope
+ *   subscribe<Subscribable> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
+ * }
+ * foo()
+ * ```
+ * `runBlocking` 不会结束, 也就是下一行 `foo()` 不会被执行. 直到监听时创建的 `Listener` 被停止.
  *
- * 将以当前协程的 job 为父 job 启动监听, 因此, 当当前协程运行结束后, 监听也会结束.
- * [handler] 将会有当前协程上下文执行, 即会被调用 [subscribe] 时的协程调度器执行
+ *
+ * 要创建一个全局都存在的监听, 即守护协程, 请在 [GlobalScope] 下调用本函数:
+ * ```kotlin
+ * GlobalScope.subscribe<Subscribable> { /* 一些处理 */ }
+ * ```
+ *
+ *
+ * 要创建一个仅在机器人在线时的监听, 请在 [Bot] 下调用本函数 (因为 [Bot] 也实现 [CoroutineScope]):
+ * ```kotlin
+ * bot.subscribe<Subscribe> { /* 一些处理 */ }
+ * ```
  */
-suspend inline fun <reified E : Subscribable> subscribe(noinline handler: suspend E.(E) -> ListeningStatus): Listener<E> = E::class.subscribe(handler)
-
-suspend inline fun <reified E : Subscribable> subscribeAlways(noinline listener: suspend E.(E) -> Unit): Listener<E> = E::class.subscribeAlways(listener)
-
-suspend inline fun <reified E : Subscribable> subscribeOnce(noinline listener: suspend E.(E) -> Unit): Listener<E> = E::class.subscribeOnce(listener)
-
-suspend inline fun <reified E : Subscribable, T> subscribeUntil(valueIfStop: T, noinline listener: suspend E.(E) -> T): Listener<E> =
-    E::class.subscribeUntil(valueIfStop, listener)
-
-suspend inline fun <reified E : Subscribable> subscribeUntilFalse(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
-    E::class.subscribeUntilFalse(listener)
-
-suspend inline fun <reified E : Subscribable> subscribeUntilTrue(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
-    E::class.subscribeUntilTrue(listener)
-
-suspend inline fun <reified E : Subscribable> subscribeUntilNull(noinline listener: suspend E.(E) -> Any?): Listener<E> = E::class.subscribeUntilNull(listener)
-
-
-suspend inline fun <reified E : Subscribable, T> subscribeWhile(valueIfContinue: T, noinline listener: suspend E.(E) -> T): Listener<E> =
-    E::class.subscribeWhile(valueIfContinue, listener)
-
-suspend inline fun <reified E : Subscribable> subscribeWhileFalse(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
-    E::class.subscribeWhileFalse(listener)
-
-suspend inline fun <reified E : Subscribable> subscribeWhileTrue(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
-    E::class.subscribeWhileTrue(listener)
-
-suspend inline fun <reified E : Subscribable> subscribeWhileNull(noinline listener: suspend E.(E) -> Any?): Listener<E> = E::class.subscribeWhileNull(listener)
-
-// endregion
-
-
-// region KClass 的扩展方法 (不推荐)
-
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribe(handler: suspend E.(E) -> ListeningStatus) = this.subscribeInternal(Handler { it.handler(it) })
+inline fun <reified E : Subscribable> CoroutineScope.subscribe(crossinline handler: suspend E.(E) -> ListeningStatus): Listener<E> =
+    E::class.subscribeInternal(Handler { it.handler(it) })
 
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribeAlways(listener: suspend E.(E) -> Unit) =
-    this.subscribeInternal(Handler { it.listener(it); ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribeOnce(listener: suspend E.(E) -> Unit) =
-    this.subscribeInternal(Handler { it.listener(it); ListeningStatus.STOPPED })
-
-@PublishedApi
-internal suspend fun <E : Subscribable, T> KClass<E>.subscribeUntil(valueIfStop: T, listener: suspend E.(E) -> T) =
-    subscribeInternal(Handler { if (it.listener(it) == valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilFalse(noinline listener: suspend E.(E) -> Boolean) = subscribeUntil(false, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilTrue(noinline listener: suspend E.(E) -> Boolean) = subscribeUntil(true, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilNull(noinline listener: suspend E.(E) -> Any?) = subscribeUntil(null, listener)
-
-
-@PublishedApi
-internal suspend fun <E : Subscribable, T> KClass<E>.subscribeWhile(valueIfContinue: T, listener: suspend E.(E) -> T) =
-    subscribeInternal(Handler { if (it.listener(it) !== valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileFalse(noinline listener: suspend E.(E) -> Boolean) = subscribeWhile(false, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileTrue(noinline listener: suspend E.(E) -> Boolean) = subscribeWhile(true, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileNull(noinline listener: suspend E.(E) -> Any?) = subscribeWhile(null, listener)
-
-// endregion
+/**
+ * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行.
+ *
+ * 仅当 [Listener] complete 时结束.
+ *
+ * @see subscribe 获取更多说明
+ */
+inline fun <reified E : Subscribable> CoroutineScope.subscribeAlways(crossinline listener: suspend E.(E) -> Unit): Listener<E> =
+    E::class.subscribeInternal(Handler { it.listener(it); ListeningStatus.LISTENING })
 
-// region ListenerBuilder DSL
+/**
+ * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 仅在第一次 [事件广播][Subscribable.broadcast] 时, [listener] 会被执行.
+ *
+ * 在这之前, 可通过 [Listener.complete] 来停止监听.
+ *
+ * @see subscribe 获取更多说明
+ */
+inline fun <reified E : Subscribable> CoroutineScope.subscribeOnce(crossinline listener: suspend E.(E) -> Unit): Listener<E> =
+    E::class.subscribeInternal(Handler { it.listener(it); ListeningStatus.STOPPED })
 
 /**
- * 监听一个事件. 可同时进行多种方式的监听
- * @see ListenerBuilder
+ * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行, 直到 [listener] 的返回值 [equals] 于 [valueIfStop]
+ *
+ * 可在任意时刻通过 [Listener.complete] 来停止监听.
+ *
+ * @see subscribe 获取更多说明
  */
-@ListenersBuilderDsl
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribeAll(listeners: suspend ListenerBuilder<E>.() -> Unit) {
-    listeners(ListenerBuilder { this.subscribeInternal(it) })
-}
+inline fun <reified E : Subscribable, T> CoroutineScope.subscribeUntil(valueIfStop: T, crossinline listener: suspend E.(E) -> T): Listener<E> =
+    E::class.subscribeInternal(Handler { if (it.listener(it) == valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
 
 /**
- * 监听一个事件. 可同时进行多种方式的监听
- * @see ListenerBuilder
+ * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
+ * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行,
+ * 如果 [listener] 的返回值 [equals] 于 [valueIfContinue], 则继续监听, 否则停止
+ *
+ * 可在任意时刻通过 [Listener.complete] 来停止监听.
+ *
+ * @see subscribe 获取更多说明
  */
-@ListenersBuilderDsl
-suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: suspend ListenerBuilder<E>.() -> Unit) = E::class.subscribeAll(listeners)
+inline fun <reified E : Subscribable, T> CoroutineScope.subscribeWhile(valueIfContinue: T, crossinline listener: suspend E.(E) -> T): Listener<E> =
+    E::class.subscribeInternal(Handler { if (it.listener(it) !== valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
+
+// endregion
 
+// region ListenerBuilder DSL
+
+/*
 /**
  * 监听构建器. 可同时进行多种方式的监听
  *
@@ -152,34 +138,34 @@ suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: s
 @ListenersBuilderDsl
 @Suppress("MemberVisibilityCanBePrivate", "unused")
 inline class ListenerBuilder<out E : Subscribable>(
-    @PublishedApi internal inline val handlerConsumer: suspend (Listener<E>) -> Unit
+    @PublishedApi internal inline val handlerConsumer: CoroutineCoroutineScope.(Listener<E>) -> Unit
 ) {
-    suspend inline fun handler(noinline listener: suspend E.(E) -> ListeningStatus) {
+    fun CoroutineCoroutineScope.handler(listener: suspend E.(E) -> ListeningStatus) {
         handlerConsumer(Handler { it.listener(it) })
     }
 
-    suspend inline fun always(noinline listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
+    fun CoroutineCoroutineScope.always(listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
 
-    suspend inline fun <T> until(until: T, noinline listener: suspend E.(E) -> T) =
+    fun <T> CoroutineCoroutineScope.until(until: T, listener: suspend E.(E) -> T) =
         handler { if (listener(it) == until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
 
-    suspend inline fun untilFalse(noinline listener: suspend E.(E) -> Boolean) = until(false, listener)
-    suspend inline fun untilTrue(noinline listener: suspend E.(E) -> Boolean) = until(true, listener)
-    suspend inline fun untilNull(noinline listener: suspend E.(E) -> Any?) = until(null, listener)
+    fun CoroutineCoroutineScope.untilFalse(listener: suspend E.(E) -> Boolean) = until(false, listener)
+    fun CoroutineCoroutineScope.untilTrue(listener: suspend E.(E) -> Boolean) = until(true, listener)
+    fun CoroutineCoroutineScope.untilNull(listener: suspend E.(E) -> Any?) = until(null, listener)
 
 
-    suspend inline fun <T> `while`(until: T, noinline listener: suspend E.(E) -> T) =
+    fun <T> CoroutineCoroutineScope.`while`(until: T, listener: suspend E.(E) -> T) =
         handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
 
-    suspend inline fun whileFalse(noinline listener: suspend E.(E) -> Boolean) = `while`(false, listener)
-    suspend inline fun whileTrue(noinline listener: suspend E.(E) -> Boolean) = `while`(true, listener)
-    suspend inline fun whileNull(noinline listener: suspend E.(E) -> Any?) = `while`(null, listener)
+    fun CoroutineCoroutineScope.whileFalse(listener: suspend E.(E) -> Boolean) = `while`(false, listener)
+    fun CoroutineCoroutineScope.whileTrue(listener: suspend E.(E) -> Boolean) = `while`(true, listener)
+    fun CoroutineCoroutineScope.whileNull(listener: suspend E.(E) -> Any?) = `while`(null, listener)
 
 
-    suspend inline fun once(noinline listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.STOPPED }
+    fun CoroutineCoroutineScope.once(listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.STOPPED }
 }
 
 @DslMarker
 annotation class ListenersBuilderDsl
-
+*/
 // endregion

+ 0 - 103
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/SubscribersWithBot.kt

@@ -1,103 +0,0 @@
-@file:Suppress("unused")
-
-package net.mamoe.mirai.event
-
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.event.internal.HandlerWithSession
-import net.mamoe.mirai.event.internal.Listener
-import net.mamoe.mirai.event.internal.subscribeInternal
-import kotlin.reflect.KClass
-
-/**
- * 该文件为所有的含 Bot 的事件的订阅方法
- *
- * 与不含 bot 的相比, 在监听时将会有 `this: Bot`
- * 在 demo 中找到实例可很快了解区别.
- */
-
-// region 顶层方法
-
-suspend inline fun <reified E : Subscribable> Bot.subscribe(noinline handler: suspend Bot.(E) -> ListeningStatus): Listener<E> =
-    E::class.subscribe(this, handler)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeAlways(noinline listener: suspend Bot.(E) -> Unit): Listener<E> =
-    E::class.subscribeAlways(this, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeOnce(noinline listener: suspend Bot.(E) -> Unit): Listener<E> =
-    E::class.subscribeOnce(this, listener)
-
-suspend inline fun <reified E : Subscribable, T> Bot.subscribeUntil(valueIfStop: T, noinline listener: suspend Bot.(E) -> T): Listener<E> =
-    E::class.subscribeUntil(this, valueIfStop, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeUntilFalse(noinline listener: suspend Bot.(E) -> Boolean): Listener<E> =
-    E::class.subscribeUntilFalse(this, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeUntilTrue(noinline listener: suspend Bot.(E) -> Boolean): Listener<E> =
-    E::class.subscribeUntilTrue(this, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeUntilNull(noinline listener: suspend Bot.(E) -> Any?): Listener<E> =
-    E::class.subscribeUntilNull(this, listener)
-
-
-suspend inline fun <reified E : Subscribable, T> Bot.subscribeWhile(valueIfContinue: T, noinline listener: suspend Bot.(E) -> T): Listener<E> =
-    E::class.subscribeWhile(this, valueIfContinue, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeWhileFalse(noinline listener: suspend Bot.(E) -> Boolean): Listener<E> =
-    E::class.subscribeWhileFalse(this, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeWhileTrue(noinline listener: suspend Bot.(E) -> Boolean): Listener<E> =
-    E::class.subscribeWhileTrue(this, listener)
-
-suspend inline fun <reified E : Subscribable> Bot.subscribeWhileNull(noinline listener: suspend Bot.(E) -> Any?): Listener<E> =
-    E::class.subscribeWhileNull(this, listener)
-
-// endregion
-
-// region KClass 的扩展方法 (仅内部使用)
-
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribe(bot: Bot, handler: suspend Bot.(E) -> ListeningStatus) =
-    this.subscribeInternal(HandlerWithSession(bot, handler))
-
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribeAlways(bot: Bot, listener: suspend Bot.(E) -> Unit) =
-    this.subscribeInternal(HandlerWithSession(bot) { listener(it); ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend fun <E : Subscribable> KClass<E>.subscribeOnce(bot: Bot, listener: suspend Bot.(E) -> Unit) =
-    this.subscribeInternal(HandlerWithSession(bot) { listener(it); ListeningStatus.STOPPED })
-
-@PublishedApi
-internal suspend fun <E : Subscribable, T> KClass<E>.subscribeUntil(bot: Bot, valueIfStop: T, listener: suspend Bot.(E) -> T) =
-    subscribeInternal(HandlerWithSession(bot) { if (listener(it) === valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilFalse(bot: Bot, noinline listener: suspend Bot.(E) -> Boolean) =
-    subscribeUntil(bot, false, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilTrue(bot: Bot, noinline listener: suspend Bot.(E) -> Boolean) =
-    subscribeUntil(bot, true, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeUntilNull(bot: Bot, noinline listener: suspend Bot.(E) -> Any?) =
-    subscribeUntil(bot, null, listener)
-
-
-@PublishedApi
-internal suspend fun <E : Subscribable, T> KClass<E>.subscribeWhile(bot: Bot, valueIfContinue: T, listener: suspend Bot.(E) -> T) =
-    subscribeInternal(HandlerWithSession(bot) { if (listener(it) !== valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileFalse(bot: Bot, noinline listener: suspend Bot.(E) -> Boolean) =
-    subscribeWhile(bot, false, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileTrue(bot: Bot, noinline listener: suspend Bot.(E) -> Boolean) =
-    subscribeWhile(bot, true, listener)
-
-@PublishedApi
-internal suspend inline fun <E : Subscribable> KClass<E>.subscribeWhileNull(bot: Bot, noinline listener: suspend Bot.(E) -> Any?) =
-    subscribeWhile(bot, null, listener)
-
-// endregion

+ 33 - 92
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt

@@ -1,22 +1,17 @@
 package net.mamoe.mirai.event.internal
 
 import kotlinx.coroutines.CompletableJob
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
-import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.Listener
 import net.mamoe.mirai.event.ListeningStatus
 import net.mamoe.mirai.event.Subscribable
-import net.mamoe.mirai.event.events.BotEvent
 import net.mamoe.mirai.utils.LockFreeLinkedList
 import net.mamoe.mirai.utils.io.logStacktrace
-import net.mamoe.mirai.utils.unsafeWeakRef
 import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.coroutineContext
 import kotlin.jvm.JvmField
 import kotlin.reflect.KClass
-import kotlin.reflect.KFunction
 
 /**
  * 设置为 `true` 以关闭事件.
@@ -24,15 +19,8 @@ import kotlin.reflect.KFunction
  */
 var EventDisabled = false
 
-/**
- * 监听和广播实现.
- * 它会首先检查这个事件是否正在被广播
- *  - 如果是, 则将新的监听者放入缓存中. 在当前广播结束后转移到主列表 (通过一个协程完成)
- *  - 如果不是, 则直接将新的监听者放入主列表
- *
- * @author Him188moe
- */ // inline to avoid a Continuation creation
-internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscribeInternal(listener: L): L {
+@PublishedApi
+internal fun <L : Listener<E>, E : Subscribable> KClass<out E>.subscribeInternal(listener: L): L {
     this.listeners().addLast(listener)
     return listener
 }
@@ -40,18 +28,15 @@ internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscr
 /**
  * 事件监听器.
  *
- * 它实现 [CompletableJob] 接口,
- * 可通过 [CompletableJob.complete] 来正常结束监听, 或 [CompletableJob.completeExceptionally] 来异常地结束监听.
- *
  * @author Him188moe
  */
-sealed class Listener<in E : Subscribable> : CompletableJob {
-    abstract suspend fun onEvent(event: E): ListeningStatus
+internal sealed class ListenerImpl<in E : Subscribable> : Listener<E> {
+    abstract override suspend fun onEvent(event: E): ListeningStatus
 }
 
 @PublishedApi
 @Suppress("FunctionName")
-internal suspend inline fun <E : Subscribable> Handler(noinline handler: suspend (E) -> ListeningStatus): Handler<E> {
+internal fun <E : Subscribable> CoroutineScope.Handler(handler: suspend (E) -> ListeningStatus): Handler<E> {
     return Handler(coroutineContext[Job], coroutineContext, handler)
 }
 
@@ -60,14 +45,15 @@ internal suspend inline fun <E : Subscribable> Handler(noinline handler: suspend
  */
 @PublishedApi
 internal class Handler<in E : Subscribable>
-@PublishedApi internal constructor(parentJob: Job?, private val context: CoroutineContext, @JvmField val handler: suspend (E) -> ListeningStatus) :
-    Listener<E>(), CompletableJob by Job(parentJob) {
+@PublishedApi internal constructor(parentJob: Job?, private val subscriberContext: CoroutineContext, @JvmField val handler: suspend (E) -> ListeningStatus) :
+    ListenerImpl<E>(), CompletableJob by Job(parentJob) {
 
     override suspend fun onEvent(event: E): ListeningStatus {
         if (isCompleted || isCancelled) return ListeningStatus.STOPPED
         if (!isActive) return ListeningStatus.LISTENING
         return try {
-            withContext(context) { handler.invoke(event) }.also { if (it == ListeningStatus.STOPPED) this.complete() }
+            // Inherit context.
+            withContext(subscriberContext) { handler.invoke(event) }.also { if (it == ListeningStatus.STOPPED) this.complete() }
         } catch (e: Throwable) {
             e.logStacktrace()
             //this.completeExceptionally(e)
@@ -76,96 +62,51 @@ internal class Handler<in E : Subscribable>
     }
 }
 
-@PublishedApi
-@Suppress("FunctionName")
-internal suspend inline fun <E : Subscribable> HandlerWithSession(
-    bot: Bot,
-    noinline handler: suspend Bot.(E) -> ListeningStatus
-): HandlerWithSession<E> {
-    return HandlerWithSession(bot, coroutineContext[Job], coroutineContext, handler)
-}
-
-/**
- * 带有 bot 筛选的监听器.
- * 所有的非 [BotEvent] 的事件都不会被处理
- * 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理
- */
-@PublishedApi
-internal class HandlerWithSession<E : Subscribable> @PublishedApi internal constructor(
-    bot: Bot,
-    parentJob: Job?, private val context: CoroutineContext, @JvmField val handler: suspend Bot.(E) -> ListeningStatus
-) : Listener<E>(), CompletableJob by Job(parentJob) {
-    val bot: Bot by bot.unsafeWeakRef()
-
-    override suspend fun onEvent(event: E): ListeningStatus {
-        if (isCompleted || isCancelled) return ListeningStatus.STOPPED
-        if (!isActive) return ListeningStatus.LISTENING
-
-        if (event !is BotEvent || event.bot !== bot) return ListeningStatus.LISTENING
-
-        return try {
-            withContext(context) { bot.handler(event) }.also { if (it == ListeningStatus.STOPPED) complete() }
-        } catch (e: Throwable) {
-            e.logStacktrace()
-            //completeExceptionally(e)
-            complete()
-            ListeningStatus.STOPPED
-        }
-    }
-}
-
 /**
  * 这个事件类的监听器 list
  */
-internal suspend fun <E : Subscribable> KClass<out E>.listeners(): EventListeners<E> = EventListenerManger.get(this)
-
-internal class EventListeners<E : Subscribable> : LockFreeLinkedList<Listener<E>>() {
-    init {
-        this::class.members.filterIsInstance<KFunction<*>>().forEach {
-            if (it.name == "add") {
-                it.isExternal
-            }
-        }
-    }
-}
+internal fun <E : Subscribable> KClass<out E>.listeners(): EventListeners<E> = EventListenerManger.get(this)
+
+internal class EventListeners<E : Subscribable> : LockFreeLinkedList<Listener<E>>()
 
 /**
  * 管理每个事件 class 的 [EventListeners].
  * [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储.
  */
 internal object EventListenerManger {
-    private val registries: MutableMap<KClass<out Subscribable>, EventListeners<*>> = mutableMapOf()
-    private val registriesMutex = Mutex()
+    private data class Registry<E : Subscribable>(val clazz: KClass<E>, val listeners: EventListeners<E>)
 
-    @Suppress("UNCHECKED_CAST")
-    internal suspend fun <E : Subscribable> get(clazz: KClass<out E>): EventListeners<E> =
-        if (registries.containsKey(clazz)) registries[clazz] as EventListeners<E>
-        else registriesMutex.withLock {
-            EventListeners<E>().let {
-                registries[clazz] = it
-                return it
-            }
-        }
+    private val registries = LockFreeLinkedList<Registry<*>>()
 
+    @Suppress("UNCHECKED_CAST")
+    internal fun <E : Subscribable> get(clazz: KClass<out E>): EventListeners<E> {
+        return registries.filteringGetOrAdd({ it.clazz == clazz }) {
+            Registry(
+                clazz,
+                EventListeners()
+            )
+        }.listeners as EventListeners<E>
+    }
 }
 
 // inline: NO extra Continuation
-internal suspend inline fun <E : Subscribable> E.broadcastInternal(): E {
-    if (EventDisabled) return this
+internal suspend inline fun Subscribable.broadcastInternal() {
+    if (EventDisabled) return
 
     callAndRemoveIfRequired(this::class.listeners())
 
     this::class.supertypes.forEach { superType ->
-        if (Subscribable::class.isInstance(superType)) {
-            // the super type is a child of Subscribable, then we can cast.
+        val superListeners =
             @Suppress("UNCHECKED_CAST")
-            callAndRemoveIfRequired((superType.classifier as KClass<out Subscribable>).listeners())
-        }
+            (superType.classifier as? KClass<out Subscribable>)?.listeners() ?: return // return if super type is not Subscribable
+
+        callAndRemoveIfRequired(superListeners)
     }
-    return this
+    return
 }
 
 private suspend inline fun <E : Subscribable> E.callAndRemoveIfRequired(listeners: EventListeners<E>) {
+    // atomic foreach
     listeners.forEach {
         if (it.onEvent(this) == ListeningStatus.STOPPED) {
             listeners.remove(it) // atomic remove

+ 44 - 0
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt

@@ -0,0 +1,44 @@
+package net.mamoe.mirai.event
+
+import kotlinx.coroutines.runBlocking
+import net.mamoe.mirai.test.shouldBeEqualTo
+import kotlin.system.exitProcess
+import kotlin.test.Test
+
+
+class TestEvent : Subscribable {
+    var triggered = false
+}
+
+class EventTests {
+    @Test
+    fun testSubscribeInplace() {
+        runBlocking {
+            val subscriber = subscribeAlways<TestEvent> {
+                triggered = true
+                println("Triggered")
+            }
+
+            TestEvent().broadcast().triggered shouldBeEqualTo true
+            subscriber.complete()
+            println("finished")
+        }
+    }
+
+    @Test
+    fun testSubscribeGlobalScope() {
+        runBlocking {
+            TestEvent().broadcast().triggered shouldBeEqualTo true
+            println("finished")
+        }
+
+    }
+
+    companion object {
+        @JvmStatic
+        fun main(args: Array<String>) {
+            EventTests().testSubscribeGlobalScope()
+            exitProcess(0)
+        }
+    }
+}

+ 13 - 28
mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt

@@ -51,17 +51,6 @@ suspend fun main() {
     bot.messageDSL()
     directlySubscribe(bot)
 
-    //DSL 监听
-    subscribeAll<FriendMessage> {
-        always {
-            //获取第一个纯文本消息
-            val firstText = it.message.firstOrNull<PlainText>()
-
-        }
-    }
-
-    demo2()
-
     bot.network.awaitDisconnection()//等到直到断开连接
 }
 
@@ -199,9 +188,22 @@ suspend fun Bot.messageDSL() {
  */
 @Suppress("UNUSED_VARIABLE")
 suspend fun directlySubscribe(bot: Bot) {
+    // 在当前协程作用域 (CoroutineScope) 下创建一个子 Job, 监听一个事件.
+    //
     // 手动处理消息
     // 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot.
     // 这样可以调用 Bot 内的一些扩展方法如 UInt.qq():QQ
+    //
+    // 这个函数返回 Listener, Listener 是一个 CompletableJob. 如果不手动 close 它, 它会一直阻止当前 CoroutineScope 结束.
+    // 例如:
+    // ```kotlin
+    // runBlocking {// this: CoroutineScope
+    //     bot.subscribeAlways<FriendMessage> {
+    //     }
+    // }
+    // ```
+    // 则这个 `runBlocking` 永远不会结束, 因为 `subscribeAlways` 在 `runBlocking` 的 `CoroutineScope` 下创建了一个 Job.
+    // 正确的用法为:
     bot.subscribeAlways<FriendMessage> {
         // this: Bot
         // it: FriendMessageEvent
@@ -245,21 +247,4 @@ suspend fun directlySubscribe(bot: Bot) {
             it.message eq "发图片2" -> it.reply(PlainText("test") + Image(ImageId("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg")))
         }
     }
-}
-
-/**
- * 实现功能:
- * 对机器人说 "记笔记", 机器人记录之后的所有消息.
- * 对机器人说 "停止", 机器人停止
- */
-suspend fun demo2() {
-    subscribeAlways<FriendMessage> { event ->
-        if (event.message eq "记笔记") {
-            subscribeUntilFalse<FriendMessage> {
-                it.reply("你发送了 ${it.message}")
-
-                it.message eq "停止"
-            }
-        }
-    }
 }

+ 9 - 6
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt

@@ -37,7 +37,7 @@ private fun readTestAccount(): BotAccount? {
     val lines = file.readLines()
     return try {
         BotAccount(lines[0].toLong(), lines[1])
-    } catch (e: Exception) {
+    } catch (e: Throwable) {
         null
     }
 }
@@ -54,16 +54,15 @@ suspend fun main() {
     /**
      * 监听所有事件
      */
-    subscribeAlways<Subscribable> {
-
-        //bot.logger.verbose("收到了一个事件: ${it::class.simpleName}")
+    GlobalScope.subscribeAlways<Subscribable> {
+        bot.logger.verbose("收到了一个事件: $this")
     }
 
-    subscribeAlways<ReceiveFriendAddRequestEvent> {
+    GlobalScope.subscribeAlways<ReceiveFriendAddRequestEvent> {
         it.approve()
     }
 
-    bot.subscribeGroupMessages {
+    GlobalScope.subscribeGroupMessages {
         "群资料" reply {
             group.updateGroupInfo().toString().reply()
         }
@@ -85,7 +84,11 @@ suspend fun main() {
     }
 
     bot.subscribeMessages {
+        always {
+        }
+
         case("at me") { At(sender).reply() }
+        // 等同于  "at me" reply { At(sender) }
 
         "你好" reply "你好!"