mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 19:32:29 +08:00
Rewrite events
This commit is contained in:
parent
e56ef7bad1
commit
22175eaabb
@ -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 }) { this@content.reply(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({ this@matchingReply.matchEntire(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({ this@findingReply.find(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) {
|
||||
this@case.reply(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,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 this@broadcast.broadcastInternal()
|
||||
} finally {
|
||||
if (EventDebuggingFlag) {
|
||||
EventDebugLogger.debug(this::class.simpleName + " after broadcast")
|
||||
}
|
||||
}
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
this@broadcast.broadcastInternal() // inline, no extra cost
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
*/
|
||||
enum class ListeningStatus {
|
||||
/**
|
||||
* 表示继续监听
|
||||
*/
|
||||
LISTENING,
|
||||
|
||||
/**
|
||||
* 表示已停止
|
||||
*/
|
||||
@JvmStatic
|
||||
val STOPPED = ListeningStatus(false)
|
||||
}
|
||||
/**
|
||||
* 表示已停止
|
||||
*/
|
||||
STOPPED
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件监听器.
|
||||
* 由 [subscribe] 等方法返回.
|
||||
*/
|
||||
interface Listener<in E : Subscribable> : CompletableJob {
|
||||
suspend fun onEvent(event: E): ListeningStatus
|
||||
}
|
||||
|
||||
// region 顶层方法
|
||||
// region 顶层方法 创建当前 coroutineContext 下的子 Job
|
||||
|
||||
/**
|
||||
* 订阅所有 [E] 及其子类事件.
|
||||
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
|
||||
* 每当 [事件广播][Subscribable.broadcast] 时, [handler] 都会被执行.
|
||||
*
|
||||
* 将以当前协程的 job 为父 job 启动监听, 因此, 当当前协程运行结束后, 监听也会结束.
|
||||
* [handler] 将会有当前协程上下文执行, 即会被调用 [subscribe] 时的协程调度器执行
|
||||
* 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听.
|
||||
* 或 [Listener] complete 时结束.
|
||||
*
|
||||
*
|
||||
* **注意**: 这个函数返回 [Listener], 它是一个 [CompletableJob]. 如果不手动 [CompletableJob.complete], 它将会阻止当前 [CoroutineScope] 结束.
|
||||
* 例如:
|
||||
* ```kotlin
|
||||
* runBlocking { // this: CoroutineScope
|
||||
* subscribe<Subscribable> { /* 一些处理 */ } // 返回 Listener, 即 CompletableJob
|
||||
* }
|
||||
* foo()
|
||||
* ```
|
||||
* `runBlocking` 不会结束, 也就是下一行 `foo()` 不会被执行. 直到监听时创建的 `Listener` 被停止.
|
||||
*
|
||||
*
|
||||
* 要创建一个全局都存在的监听, 即守护协程, 请在 [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)
|
||||
/**
|
||||
* 在指定的 [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 })
|
||||
|
||||
suspend inline fun <reified E : Subscribable> subscribeOnce(noinline listener: suspend E.(E) -> Unit): Listener<E> = E::class.subscribeOnce(listener)
|
||||
/**
|
||||
* 在指定的 [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 })
|
||||
|
||||
suspend inline fun <reified E : Subscribable, T> subscribeUntil(valueIfStop: T, noinline listener: suspend E.(E) -> T): Listener<E> =
|
||||
E::class.subscribeUntil(valueIfStop, listener)
|
||||
/**
|
||||
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
|
||||
* 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行, 直到 [listener] 的返回值 [equals] 于 [valueIfStop]
|
||||
*
|
||||
* 可在任意时刻通过 [Listener.complete] 来停止监听.
|
||||
*
|
||||
* @see subscribe 获取更多说明
|
||||
*/
|
||||
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 })
|
||||
|
||||
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) =
|
||||
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)
|
||||
/**
|
||||
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
|
||||
* 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行,
|
||||
* 如果 [listener] 的返回值 [equals] 于 [valueIfContinue], 则继续监听, 否则停止
|
||||
*
|
||||
* 可在任意时刻通过 [Listener.complete] 来停止监听.
|
||||
*
|
||||
* @see subscribe 获取更多说明
|
||||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
* 监听一个事件. 可同时进行多种方式的监听
|
||||
* @see ListenerBuilder
|
||||
*/
|
||||
@ListenersBuilderDsl
|
||||
@PublishedApi
|
||||
internal suspend fun <E : Subscribable> KClass<E>.subscribeAll(listeners: suspend ListenerBuilder<E>.() -> Unit) {
|
||||
listeners(ListenerBuilder { this.subscribeInternal(it) })
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听一个事件. 可同时进行多种方式的监听
|
||||
* @see ListenerBuilder
|
||||
*/
|
||||
@ListenersBuilderDsl
|
||||
suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: suspend ListenerBuilder<E>.() -> Unit) = E::class.subscribeAll(listeners)
|
||||
|
||||
/*
|
||||
/**
|
||||
* 监听构建器. 可同时进行多种方式的监听
|
||||
*
|
||||
@ -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
|
@ -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
|
@ -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 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 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>)
|
||||
|
||||
private val registries = LockFreeLinkedList<Registry<*>>()
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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 "停止"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 "你好!"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user