Rewrite events

This commit is contained in:
Him188 2019-12-23 22:28:35 +08:00
parent e56ef7bad1
commit 22175eaabb
8 changed files with 267 additions and 447 deletions

View File

@ -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) }
*/
}

View File

@ -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")
}
}
this@broadcast.broadcastInternal() // inline, no extra cost
return this
}
/**

View File

@ -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 {
*/
enum class ListeningStatus {
/**
* 表示继续监听
*/
@JvmStatic
val LISTENING = ListeningStatus(true)
LISTENING,
/**
* 表示已停止
*/
@JvmStatic
val STOPPED = ListeningStatus(false)
STOPPED
}
}
// region 顶层方法
/**
* 订阅所有 [E] 及其子类事件.
*
* 将以当前协程的 job 为父 job 启动监听, 因此, 当当前协程运行结束后, 监听也会结束.
* [handler] 将会有当前协程上下文执行, 即会被调用 [subscribe] 时的协程调度器执行
* 事件监听器.
* [subscribe] 等方法返回.
*/
suspend inline fun <reified E : Subscribable> subscribe(noinline handler: suspend E.(E) -> ListeningStatus): Listener<E> = E::class.subscribe(handler)
interface Listener<in E : Subscribable> : CompletableJob {
suspend fun onEvent(event: E): ListeningStatus
}
suspend inline fun <reified E : Subscribable> subscribeAlways(noinline listener: suspend E.(E) -> Unit): Listener<E> = E::class.subscribeAlways(listener)
// region 顶层方法 创建当前 coroutineContext 下的子 Job
suspend inline fun <reified E : Subscribable> subscribeOnce(noinline listener: suspend E.(E) -> Unit): Listener<E> = E::class.subscribeOnce(listener)
/**
* 在指定的 [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` 被停止.
*
*
* 要创建一个全局都存在的监听, 即守护协程, 请在 [GlobalScope] 下调用本函数:
* ```kotlin
* GlobalScope.subscribe<Subscribable> { /* 一些处理 */ }
* ```
*
*
* 要创建一个仅在机器人在线时的监听, 请在 [Bot] 下调用本函数 (因为 [Bot] 也实现 [CoroutineScope]):
* ```kotlin
* bot.subscribe<Subscribe> { /* 一些处理 */ }
* ```
*/
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, T> subscribeUntil(valueIfStop: T, noinline listener: suspend E.(E) -> T): Listener<E> =
E::class.subscribeUntil(valueIfStop, 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> subscribeUntilFalse(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
E::class.subscribeUntilFalse(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> subscribeUntilTrue(noinline listener: suspend E.(E) -> Boolean): Listener<E> =
E::class.subscribeUntilTrue(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> 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

View File

@ -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

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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
@ -246,20 +248,3 @@ suspend fun directlySubscribe(bot: Bot) {
}
}
}
/**
* 实现功能:
* 对机器人说 "记笔记", 机器人记录之后的所有消息.
* 对机器人说 "停止", 机器人停止
*/
suspend fun demo2() {
subscribeAlways<FriendMessage> { event ->
if (event.message eq "记笔记") {
subscribeUntilFalse<FriendMessage> {
it.reply("你发送了 ${it.message}")
it.message eq "停止"
}
}
}
}

View File

@ -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 "你好!"