Commit 22175eaa authored by Him188's avatar Him188

Rewrite events

parent e56ef7ba
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
package net.mamoe.mirai.event package net.mamoe.mirai.event
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.isAdministrator import net.mamoe.mirai.contact.isAdministrator
import net.mamoe.mirai.contact.isOperator import net.mamoe.mirai.contact.isOperator
...@@ -13,20 +14,19 @@ import net.mamoe.mirai.message.data.Message ...@@ -13,20 +14,19 @@ import net.mamoe.mirai.message.data.Message
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.jvm.JvmName
/** /**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话. * 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) { inline fun CoroutineScope.subscribeMessages(crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<MessagePacket<*, *>> { listener -> MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
subscribeAlways<MessagePacket<*, *>> { subscribeAlways {
listener(it) listener(it, this.message.toString())
} }
}.apply { listeners() } }.apply { listeners() }
} }
...@@ -36,13 +36,13 @@ suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubsc ...@@ -36,13 +36,13 @@ suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubsc
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun subscribeGroupMessages(crossinline listeners: suspend MessageSubscribersBuilder<GroupMessage>.() -> Unit) { inline fun CoroutineScope.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<GroupMessage> { listener -> MessageSubscribersBuilder<GroupMessage> { listener ->
subscribeAlways<GroupMessage> { subscribeAlways {
listener(it) listener(it, this.message.toString())
} }
}.apply { listeners() } }.apply { listeners() }
} }
...@@ -52,13 +52,13 @@ suspend inline fun subscribeGroupMessages(crossinline listeners: suspend Message ...@@ -52,13 +52,13 @@ suspend inline fun subscribeGroupMessages(crossinline listeners: suspend Message
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun subscribeFriendMessages(crossinline listeners: suspend MessageSubscribersBuilder<FriendMessage>.() -> Unit) { inline fun CoroutineScope.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<FriendMessage> { listener -> MessageSubscribersBuilder<FriendMessage> { listener ->
subscribeAlways<FriendMessage> { subscribeAlways {
listener(it) listener(it, this.message.toString())
} }
}.apply { listeners() } }.apply { listeners() }
} }
...@@ -68,13 +68,13 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag ...@@ -68,13 +68,13 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) { inline fun Bot.subscribeMessages(crossinline listeners: MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<MessagePacket<*, *>> { listener -> MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
this.subscribeAlways<MessagePacket<*, *>> { this.subscribeAlways {
listener(it) listener(it, this.message.toString())
} }
}.apply { listeners() } }.apply { listeners() }
} }
...@@ -84,13 +84,13 @@ suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageS ...@@ -84,13 +84,13 @@ suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageS
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend MessageSubscribersBuilder<GroupMessage>.() -> Unit) { inline fun Bot.subscribeGroupMessages(crossinline listeners: MessageSubscribersBuilder<GroupMessage>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<GroupMessage> { listener -> MessageSubscribersBuilder<GroupMessage> { listener ->
this.subscribeAlways<GroupMessage> { this.subscribeAlways {
listener(it) listener(it, this.message.toString())
} }
}.apply { listeners() } }.apply { listeners() }
} }
...@@ -100,45 +100,36 @@ suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend Mes ...@@ -100,45 +100,36 @@ suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend Mes
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
@MessageDsl @MessageDsl
suspend inline fun Bot.subscribeFriendMessages(crossinline listeners: suspend MessageSubscribersBuilder<FriendMessage>.() -> Unit) { inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscribersBuilder<FriendMessage>.() -> Unit) {
contract { contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE) callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
} }
MessageSubscribersBuilder<FriendMessage> { listener -> MessageSubscribersBuilder<FriendMessage> { listener ->
this.subscribeAlways<FriendMessage> { this.subscribeAlways {
listener(it) it.listener(it.message.toString())
} }
}.apply { listeners() } }.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 * @see subscribeFriendMessages
* @sample demo.subscribe.messageDSL * @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") @Suppress("unused")
@MessageDsl @MessageDsl
class MessageSubscribersBuilder<T : MessagePacket<*, *>>( class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
inline val subscriber: suspend (@MessageDsl suspend T.(String) -> Unit) -> Unit val subscriber: (@MessageDsl suspend T.(String) -> Unit) -> Listener<T>
) { ) {
/** /**
* 无任何触发条件. * 无任何触发条件.
*/ */
@MessageDsl @MessageDsl
suspend inline fun always(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) { inline fun always(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> {
content({ true }, onEvent) return content({ true }, onEvent)
} // TODO: 2019/12/4 这些 onEvent 都应该为 cross-inline, 而这会导致一个 CompilationException }
/** /**
* 如果消息内容 `==` [equals], 就执行 [onEvent] * 如果消息内容 `==` [equals], 就执行 [onEvent]
...@@ -146,97 +137,97 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -146,97 +137,97 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param ignoreCase `true` 则不区分大小写 * @param ignoreCase `true` 则不区分大小写
*/ */
@MessageDsl @MessageDsl
suspend inline fun case( inline fun case(
equals: String, equals: String,
trim: Boolean = true, trim: Boolean = true,
ignoreCase: Boolean = false, 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 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] * 如果消息内容包含 [sub], 就执行 [onEvent]
*/ */
@MessageDsl @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] * 如果消息的前缀是 [prefix], 就执行 [onEvent]
*/ */
@MessageDsl @MessageDsl
suspend inline fun startsWith( inline fun startsWith(
prefix: String, prefix: String,
removePrefix: Boolean = true, removePrefix: Boolean = true,
noinline onEvent: @MessageDsl suspend T.(String) -> Unit crossinline onEvent: @MessageDsl suspend T.(String) -> Unit
) = ): Listener<T> =
content({ it.startsWith(prefix) }) { content({ it.startsWith(prefix) }) {
if (removePrefix) this.onEvent(this.message.stringValue.substringAfter(prefix)) if (removePrefix) this.onEvent(this.message.stringValue.substringAfter(prefix))
else onEvent(this) else onEvent(this, this.message.toString())
} }
/** /**
* 如果消息的结尾是 [suffix], 就执行 [onEvent] * 如果消息的结尾是 [suffix], 就执行 [onEvent]
*/ */
@MessageDsl @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) content({ it.endsWith(suffix) }, onEvent)
/** /**
* 如果是这个人发的消息, 就执行 [onEvent]. 消息可以是好友消息也可以是群消息 * 如果是这个人发的消息, 就执行 [onEvent]. 消息可以是好友消息也可以是群消息
*/ */
@MessageDsl @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) content({ sender.id == qqId }, onEvent)
/** /**
* 如果是管理员或群主发的消息, 就执行 [onEvent] * 如果是管理员或群主发的消息, 就执行 [onEvent]
*/ */
@MessageDsl @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) content({ this is GroupMessage && sender.permission.isOperator() }, onEvent)
/** /**
* 如果是管理员发的消息, 就执行 [onEvent] * 如果是管理员发的消息, 就执行 [onEvent]
*/ */
@MessageDsl @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) content({ this is GroupMessage && sender.permission.isAdministrator() }, onEvent)
/** /**
* 如果是群主发的消息, 就执行 [onEvent] * 如果是群主发的消息, 就执行 [onEvent]
*/ */
@MessageDsl @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) content({ this is GroupMessage && sender.permission.isOwner() }, onEvent)
/** /**
* 如果是来自这个群的消息, 就执行 [onEvent] * 如果是来自这个群的消息, 就执行 [onEvent]
*/ */
@MessageDsl @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) content({ if (this is GroupMessage) group.id == id else false }, onEvent)
/** /**
* 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent] * 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent]
*/ */
@MessageDsl @MessageDsl
suspend inline fun <reified M : Message> has(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) = 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) } subscriber { if (message.any { it::class == M::class }) onEvent(this, this.message.toString()) }
/** /**
* 如果 [filter] 返回 `true` 就执行 `onEvent` * 如果 [filter] 返回 `true` 就执行 `onEvent`
*/ */
@MessageDsl @MessageDsl
suspend inline fun content(noinline filter: T.(String) -> Boolean, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) = inline fun content(crossinline filter: T.(String) -> Boolean, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
subscriber { if (this.filter(message.stringValue)) onEvent(this) } subscriber { if (this.filter(message.toString())) onEvent(this, this.message.toString()) }
/** /**
* 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent` * 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent`
*/ */
@MessageDsl @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) content({ regex.matchEntire(it) != null }, onEvent)
} }
...@@ -244,7 +235,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -244,7 +235,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent` * 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent`
*/ */
@MessageDsl @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) content({ regex.find(it) != null }, onEvent)
} }
...@@ -252,7 +243,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -252,7 +243,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* 若消息内容包含 [this] 则回复 [reply] * 若消息内容包含 [this] 则回复 [reply]
*/ */
@MessageDsl @MessageDsl
suspend inline infix fun String.containsReply(reply: String) = infix fun String.containsReply(reply: String) =
content({ this@containsReply in it }) { this@content.reply(reply) } content({ this@containsReply in it }) { this@content.reply(reply) }
/** /**
...@@ -263,7 +254,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -263,7 +254,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复 * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/ */
@MessageDsl @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 }) { content({ this@containsReply in it }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier) executeAndReply(replier)
...@@ -277,7 +268,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -277,7 +268,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复 * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/ */
@MessageDsl @MessageDsl
suspend inline infix fun Regex.matchingReply(noinline replier: AnyReplier<T>) { inline infix fun Regex.matchingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
content({ this@matchingReply.matchEntire(it) != null }) { content({ this@matchingReply.matchEntire(it) != null }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier) executeAndReply(replier)
...@@ -292,7 +283,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -292,7 +283,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复 * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/ */
@MessageDsl @MessageDsl
suspend inline infix fun Regex.findingReply(noinline replier: AnyReplier<T>) { inline infix fun Regex.findingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
content({ this@findingReply.find(it) != null }) { content({ this@findingReply.find(it) != null }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier) executeAndReply(replier)
...@@ -313,7 +304,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -313,7 +304,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复 * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
*/ */
@MessageDsl @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() val toCheck = this.trimStart()
content({ it.trimStart().startsWith(toCheck) }) { content({ it.trimStart().startsWith(toCheck) }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
...@@ -337,7 +328,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -337,7 +328,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复 * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/ */
@MessageDsl @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() val toCheck = this.trimEnd()
content({ it.endsWith(this@endswithReply) }) { content({ it.endsWith(this@endswithReply) }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
...@@ -348,20 +339,20 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -348,20 +339,20 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
} }
@MessageDsl @MessageDsl
suspend inline infix fun String.reply(reply: String) = case(this) { infix fun String.reply(reply: String) = case(this) {
this@case.reply(reply) this@case.reply(reply)
} }
@MessageDsl @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 @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier) executeAndReply(replier)
} }
@PublishedApi @PublishedApi
@Suppress("NOTHING_TO_INLINE") @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") // false positive
internal suspend inline fun T.executeAndReply(noinline replier: AnyReplier<T>) { internal suspend inline fun T.executeAndReply(replier: @MessageDsl suspend T.(String) -> Any?) {
when (val message = replier(this)) { when (val message = replier(this, this.message.toString())) {
is Message -> this.reply(message) is Message -> this.reply(message)
is Unit -> { is Unit -> {
...@@ -371,10 +362,10 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>( ...@@ -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)) } fun replyCase(equals: String, trim: Boolean = true, 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) } fun replyContains(value: String, 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) } fun replyStartsWith(value: String, 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 replyEndsWith(value: String, replier: MessageReplier<T>) = content({ it.endsWith(value) }) { replier(this) }
*/ */
} }
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
package net.mamoe.mirai.event package net.mamoe.mirai.event
import net.mamoe.mirai.event.internal.broadcastInternal 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 ...@@ -13,9 +10,7 @@ import net.mamoe.mirai.utils.withSwitch
* 可以是任何 class 或 object. * 可以是任何 class 或 object.
* *
* @see subscribeAlways * @see subscribeAlways
* @see subscribeOnce
* @see subscribeWhile * @see subscribeWhile
* @see subscribeAll
* *
* @see subscribeMessages * @see subscribeMessages
*/ */
...@@ -47,19 +42,6 @@ abstract class Event : Subscribable { ...@@ -47,19 +42,6 @@ abstract class Event : Subscribable {
fun cancel() { fun cancel() {
cancelled = true 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 { ...@@ -74,19 +56,10 @@ interface Cancellable : Subscribable {
/** /**
* 广播一个事件的唯一途径. * 广播一个事件的唯一途径.
*/ */
@Suppress("UNCHECKED_CAST")
suspend fun <E : Subscribable> E.broadcast(): E { suspend fun <E : Subscribable> E.broadcast(): E {
if (EventDebuggingFlag) {
EventDebugLogger.debug(this::class.simpleName + " pre broadcast")
}
try {
@Suppress("EXPERIMENTAL_API_USAGE") @Suppress("EXPERIMENTAL_API_USAGE")
return this@broadcast.broadcastInternal() this@broadcast.broadcastInternal() // inline, no extra cost
} finally { return this
if (EventDebuggingFlag) {
EventDebugLogger.debug(this::class.simpleName + " after broadcast")
}
}
} }
/** /**
......
@file:Suppress("unused")
package net.mamoe.mirai.event 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.Handler
import net.mamoe.mirai.event.internal.Listener
import net.mamoe.mirai.event.internal.subscribeInternal import net.mamoe.mirai.event.internal.subscribeInternal
import kotlin.jvm.JvmStatic
import kotlin.reflect.KClass
/* /*
* 该文件为所有的订阅事件的方法. * 该文件为所有的订阅事件的方法.
...@@ -14,125 +13,112 @@ import kotlin.reflect.KClass ...@@ -14,125 +13,112 @@ import kotlin.reflect.KClass
/** /**
* 订阅者的状态 * 订阅者的状态
*/ // Not using enum for Android */
inline class ListeningStatus(inline val listening: Boolean) { enum class ListeningStatus {
companion object {
/** /**
* 表示继续监听 * 表示继续监听
*/ */
@JvmStatic LISTENING,
val LISTENING = ListeningStatus(true)
/** /**
* 表示已停止 * 表示已停止
*/ */
@JvmStatic STOPPED
val STOPPED = ListeningStatus(false)
}
} }
/**
* 事件监听器.
* 由 [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) inline fun <reified E : Subscribable> CoroutineScope.subscribe(crossinline handler: suspend E.(E) -> ListeningStatus): Listener<E> =
E::class.subscribeInternal(Handler { it.handler(it) })
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) })
@PublishedApi /**
internal suspend fun <E : Subscribable> KClass<E>.subscribeAlways(listener: suspend E.(E) -> Unit) = * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
this.subscribeInternal(Handler { it.listener(it); ListeningStatus.LISTENING }) * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行.
*
@PublishedApi * 仅当 [Listener] complete 时结束.
internal suspend fun <E : Subscribable> KClass<E>.subscribeOnce(listener: suspend E.(E) -> Unit) = *
this.subscribeInternal(Handler { it.listener(it); ListeningStatus.STOPPED }) * @see subscribe 获取更多说明
*/
@PublishedApi inline fun <reified E : Subscribable> CoroutineScope.subscribeAlways(crossinline listener: suspend E.(E) -> Unit): Listener<E> =
internal suspend fun <E : Subscribable, T> KClass<E>.subscribeUntil(valueIfStop: T, listener: suspend E.(E) -> T) = E::class.subscribeInternal(Handler { it.listener(it); ListeningStatus.LISTENING })
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
// 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 })
/** /**
* 监听一个事件. 可同时进行多种方式的监听 * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* @see ListenerBuilder * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行, 直到 [listener] 的返回值 [equals] 于 [valueIfStop]
*
* 可在任意时刻通过 [Listener.complete] 来停止监听.
*
* @see subscribe 获取更多说明
*/ */
@ListenersBuilderDsl inline fun <reified E : Subscribable, T> CoroutineScope.subscribeUntil(valueIfStop: T, crossinline listener: suspend E.(E) -> T): Listener<E> =
@PublishedApi E::class.subscribeInternal(Handler { if (it.listener(it) == valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
internal suspend fun <E : Subscribable> KClass<E>.subscribeAll(listeners: suspend ListenerBuilder<E>.() -> Unit) {
listeners(ListenerBuilder { this.subscribeInternal(it) })
}
/** /**
* 监听一个事件. 可同时进行多种方式的监听 * 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* @see ListenerBuilder * 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行,
* 如果 [listener] 的返回值 [equals] 于 [valueIfContinue], 则继续监听, 否则停止
*
* 可在任意时刻通过 [Listener.complete] 来停止监听.
*
* @see subscribe 获取更多说明
*/ */
@ListenersBuilderDsl inline fun <reified E : Subscribable, T> CoroutineScope.subscribeWhile(valueIfContinue: T, crossinline listener: suspend E.(E) -> T): Listener<E> =
suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: suspend ListenerBuilder<E>.() -> Unit) = E::class.subscribeAll(listeners) 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 ...@@ -152,34 +138,34 @@ suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: s
@ListenersBuilderDsl @ListenersBuilderDsl
@Suppress("MemberVisibilityCanBePrivate", "unused") @Suppress("MemberVisibilityCanBePrivate", "unused")
inline class ListenerBuilder<out E : Subscribable>( 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) }) 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 } handler { if (listener(it) == until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
suspend inline fun untilFalse(noinline listener: suspend E.(E) -> Boolean) = until(false, listener) fun CoroutineCoroutineScope.untilFalse(listener: suspend E.(E) -> Boolean) = until(false, listener)
suspend inline fun untilTrue(noinline listener: suspend E.(E) -> Boolean) = until(true, listener) fun CoroutineCoroutineScope.untilTrue(listener: suspend E.(E) -> Boolean) = until(true, listener)
suspend inline fun untilNull(noinline listener: suspend E.(E) -> Any?) = until(null, 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 } handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
suspend inline fun whileFalse(noinline listener: suspend E.(E) -> Boolean) = `while`(false, listener) fun CoroutineCoroutineScope.whileFalse(listener: suspend E.(E) -> Boolean) = `while`(false, listener)
suspend inline fun whileTrue(noinline listener: suspend E.(E) -> Boolean) = `while`(true, listener) fun CoroutineCoroutineScope.whileTrue(listener: suspend E.(E) -> Boolean) = `while`(true, listener)
suspend inline fun whileNull(noinline listener: suspend E.(E) -> Any?) = `while`(null, 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 @DslMarker
annotation class ListenersBuilderDsl annotation class ListenersBuilderDsl
*/
// endregion // endregion
\ No newline at end of file
@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
\ No newline at end of file
package net.mamoe.mirai.event.internal package net.mamoe.mirai.event.internal
import kotlinx.coroutines.CompletableJob import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext 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.ListeningStatus
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.utils.LockFreeLinkedList import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.io.logStacktrace import net.mamoe.mirai.utils.io.logStacktrace
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KFunction
/** /**
* 设置为 `true` 以关闭事件. * 设置为 `true` 以关闭事件.
...@@ -24,15 +19,8 @@ import kotlin.reflect.KFunction ...@@ -24,15 +19,8 @@ import kotlin.reflect.KFunction
*/ */
var EventDisabled = false var EventDisabled = false
/** @PublishedApi
* 监听和广播实现. internal fun <L : Listener<E>, E : Subscribable> KClass<out E>.subscribeInternal(listener: L): L {
* 它会首先检查这个事件是否正在被广播
* - 如果是, 则将新的监听者放入缓存中. 在当前广播结束后转移到主列表 (通过一个协程完成)
* - 如果不是, 则直接将新的监听者放入主列表
*
* @author Him188moe
*/ // inline to avoid a Continuation creation
internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscribeInternal(listener: L): L {
this.listeners().addLast(listener) this.listeners().addLast(listener)
return listener return listener
} }
...@@ -40,18 +28,15 @@ internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscr ...@@ -40,18 +28,15 @@ internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscr
/** /**
* 事件监听器. * 事件监听器.
* *
* 它实现 [CompletableJob] 接口,
* 可通过 [CompletableJob.complete] 来正常结束监听, 或 [CompletableJob.completeExceptionally] 来异常地结束监听.
*
* @author Him188moe * @author Him188moe
*/ */
sealed class Listener<in E : Subscribable> : CompletableJob { internal sealed class ListenerImpl<in E : Subscribable> : Listener<E> {
abstract suspend fun onEvent(event: E): ListeningStatus abstract override suspend fun onEvent(event: E): ListeningStatus
} }
@PublishedApi @PublishedApi
@Suppress("FunctionName") @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) return Handler(coroutineContext[Job], coroutineContext, handler)
} }
...@@ -60,14 +45,15 @@ internal suspend inline fun <E : Subscribable> Handler(noinline handler: suspend ...@@ -60,14 +45,15 @@ internal suspend inline fun <E : Subscribable> Handler(noinline handler: suspend
*/ */
@PublishedApi @PublishedApi
internal class Handler<in E : Subscribable> internal class Handler<in E : Subscribable>
@PublishedApi internal constructor(parentJob: Job?, private val context: CoroutineContext, @JvmField val handler: suspend (E) -> ListeningStatus) : @PublishedApi internal constructor(parentJob: Job?, private val subscriberContext: CoroutineContext, @JvmField val handler: suspend (E) -> ListeningStatus) :
Listener<E>(), CompletableJob by Job(parentJob) { ListenerImpl<E>(), CompletableJob by Job(parentJob) {
override suspend fun onEvent(event: E): ListeningStatus { override suspend fun onEvent(event: E): ListeningStatus {
if (isCompleted || isCancelled) return ListeningStatus.STOPPED if (isCompleted || isCancelled) return ListeningStatus.STOPPED
if (!isActive) return ListeningStatus.LISTENING if (!isActive) return ListeningStatus.LISTENING
return try { 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) { } catch (e: Throwable) {
e.logStacktrace() e.logStacktrace()
//this.completeExceptionally(e) //this.completeExceptionally(e)
...@@ -76,96 +62,51 @@ internal class Handler<in E : Subscribable> ...@@ -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 * 这个事件类的监听器 list
*/ */
internal suspend fun <E : Subscribable> KClass<out E>.listeners(): EventListeners<E> = EventListenerManger.get(this) internal fun <E : Subscribable> KClass<out E>.listeners(): EventListeners<E> = EventListenerManger.get(this)
internal class EventListeners<E : Subscribable> : LockFreeLinkedList<Listener<E>>() { internal class EventListeners<E : Subscribable> : LockFreeLinkedList<Listener<E>>()
init {
this::class.members.filterIsInstance<KFunction<*>>().forEach {
if (it.name == "add") {
it.isExternal
}
}
}
}
/** /**
* 管理每个事件 class 的 [EventListeners]. * 管理每个事件 class 的 [EventListeners].
* [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储. * [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储.
*/ */
internal object EventListenerManger { internal object EventListenerManger {
private val registries: MutableMap<KClass<out Subscribable>, EventListeners<*>> = mutableMapOf() private data class Registry<E : Subscribable>(val clazz: KClass<E>, val listeners: EventListeners<E>)
private val registriesMutex = Mutex()
private val registries = LockFreeLinkedList<Registry<*>>()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal suspend fun <E : Subscribable> get(clazz: KClass<out E>): EventListeners<E> = internal fun <E : Subscribable> get(clazz: KClass<out E>): EventListeners<E> {
if (registries.containsKey(clazz)) registries[clazz] as EventListeners<E> return registries.filteringGetOrAdd({ it.clazz == clazz }) {
else registriesMutex.withLock { Registry(
EventListeners<E>().let { clazz,
registries[clazz] = it EventListeners()
return it )
}.listeners as EventListeners<E>
} }
}
} }
// inline: NO extra Continuation // inline: NO extra Continuation
internal suspend inline fun <E : Subscribable> E.broadcastInternal(): E { internal suspend inline fun Subscribable.broadcastInternal() {
if (EventDisabled) return this if (EventDisabled) return
callAndRemoveIfRequired(this::class.listeners()) callAndRemoveIfRequired(this::class.listeners())
this::class.supertypes.forEach { superType -> this::class.supertypes.forEach { superType ->
if (Subscribable::class.isInstance(superType)) { val superListeners =
// the super type is a child of Subscribable, then we can cast.
@Suppress("UNCHECKED_CAST") @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>) { private suspend inline fun <E : Subscribable> E.callAndRemoveIfRequired(listeners: EventListeners<E>) {
// atomic foreach
listeners.forEach { listeners.forEach {
if (it.onEvent(this) == ListeningStatus.STOPPED) { if (it.onEvent(this) == ListeningStatus.STOPPED) {
listeners.remove(it) // atomic remove listeners.remove(it) // atomic remove
......
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)
}
}
}
\ No newline at end of file
...@@ -51,17 +51,6 @@ suspend fun main() { ...@@ -51,17 +51,6 @@ suspend fun main() {
bot.messageDSL() bot.messageDSL()
directlySubscribe(bot) directlySubscribe(bot)
//DSL 监听
subscribeAll<FriendMessage> {
always {
//获取第一个纯文本消息
val firstText = it.message.firstOrNull<PlainText>()
}
}
demo2()
bot.network.awaitDisconnection()//等到直到断开连接 bot.network.awaitDisconnection()//等到直到断开连接
} }
...@@ -199,9 +188,22 @@ suspend fun Bot.messageDSL() { ...@@ -199,9 +188,22 @@ suspend fun Bot.messageDSL() {
*/ */
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
suspend fun directlySubscribe(bot: Bot) { suspend fun directlySubscribe(bot: Bot) {
// 在当前协程作用域 (CoroutineScope) 下创建一个子 Job, 监听一个事件.
//
// 手动处理消息 // 手动处理消息
// 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot. // 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot.
// 这样可以调用 Bot 内的一些扩展方法如 UInt.qq():QQ // 这样可以调用 Bot 内的一些扩展方法如 UInt.qq():QQ
//
// 这个函数返回 Listener, Listener 是一个 CompletableJob. 如果不手动 close 它, 它会一直阻止当前 CoroutineScope 结束.
// 例如:
// ```kotlin
// runBlocking {// this: CoroutineScope
// bot.subscribeAlways<FriendMessage> {
// }
// }
// ```
// 则这个 `runBlocking` 永远不会结束, 因为 `subscribeAlways` 在 `runBlocking` 的 `CoroutineScope` 下创建了一个 Job.
// 正确的用法为:
bot.subscribeAlways<FriendMessage> { bot.subscribeAlways<FriendMessage> {
// this: Bot // this: Bot
// it: FriendMessageEvent // it: FriendMessageEvent
...@@ -246,20 +248,3 @@ suspend fun directlySubscribe(bot: Bot) { ...@@ -246,20 +248,3 @@ suspend fun directlySubscribe(bot: Bot) {
} }
} }
} }
\ No newline at end of file
/**
* 实现功能:
* 对机器人说 "记笔记", 机器人记录之后的所有消息.
* 对机器人说 "停止", 机器人停止
*/
suspend fun demo2() {
subscribeAlways<FriendMessage> { event ->
if (event.message eq "记笔记") {
subscribeUntilFalse<FriendMessage> {
it.reply("你发送了 ${it.message}")
it.message eq "停止"
}
}
}
}
\ No newline at end of file
...@@ -37,7 +37,7 @@ private fun readTestAccount(): BotAccount? { ...@@ -37,7 +37,7 @@ private fun readTestAccount(): BotAccount? {
val lines = file.readLines() val lines = file.readLines()
return try { return try {
BotAccount(lines[0].toLong(), lines[1]) BotAccount(lines[0].toLong(), lines[1])
} catch (e: Exception) { } catch (e: Throwable) {
null null
} }
} }
...@@ -54,16 +54,15 @@ suspend fun main() { ...@@ -54,16 +54,15 @@ suspend fun main() {
/** /**
* 监听所有事件 * 监听所有事件
*/ */
subscribeAlways<Subscribable> { GlobalScope.subscribeAlways<Subscribable> {
bot.logger.verbose("收到了一个事件: $this")
//bot.logger.verbose("收到了一个事件: ${it::class.simpleName}")
} }
subscribeAlways<ReceiveFriendAddRequestEvent> { GlobalScope.subscribeAlways<ReceiveFriendAddRequestEvent> {
it.approve() it.approve()
} }
bot.subscribeGroupMessages { GlobalScope.subscribeGroupMessages {
"群资料" reply { "群资料" reply {
group.updateGroupInfo().toString().reply() group.updateGroupInfo().toString().reply()
} }
...@@ -85,7 +84,11 @@ suspend fun main() { ...@@ -85,7 +84,11 @@ suspend fun main() {
} }
bot.subscribeMessages { bot.subscribeMessages {
always {
}
case("at me") { At(sender).reply() } case("at me") { At(sender).reply() }
// 等同于 "at me" reply { At(sender) }
"你好" reply "你好!" "你好" reply "你好!"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment