diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt index 115c95c3e..e73d3e922 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/linear.kt @@ -9,3 +9,104 @@ package net.mamoe.mirai.event +import kotlinx.coroutines.* +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * 监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +@MiraiExperimentalAPI +suspend inline fun subscribingGet( + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): R { + require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } + return subscribingGetOrNull(timeoutMillis, filter) ?: error("timeout subscribingGet") +} + +/** + * 监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + * + * @see subscribingGetAsync 本函数的异步版本 + */ +@MiraiExperimentalAPI +suspend inline fun subscribingGetOrNull( + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): R? { + require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" } + var result: R? = null + var resultThrowable: Throwable? = null + + if (timeoutMillis == -1L) { + @Suppress("DuplicatedCode") // for better performance + coroutineScope { + var listener: Listener? = null + listener = this.subscribe { + val value = try { + filter.invoke(this, it) + } catch (e: Exception) { + resultThrowable = e + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } + + if (value != null) { + result = value + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } else return@subscribe ListeningStatus.LISTENING + } + } + } else { + withTimeoutOrNull(timeoutMillis) { + var listener: Listener? = null + @Suppress("DuplicatedCode") // for better performance + listener = this.subscribe { + val value = try { + filter.invoke(this, it) + } catch (e: Exception) { + resultThrowable = e + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } + + if (value != null) { + result = value + return@subscribe ListeningStatus.STOPPED.also { listener!!.complete() } + } else return@subscribe ListeningStatus.LISTENING + } + } + } + resultThrowable?.let { throw it } + return result +} + +/** + * 异步监听这个事件, 并尝试从这个事件中获取一个值. + * + * 若 [filter] 抛出了一个异常, [Deferred.await] 会抛出这个异常或. + * + * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 + * @param coroutineContext 额外的 [CoroutineContext] + * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [subscribingGet] 会返回这个值 + */ +@MiraiExperimentalAPI +inline fun CoroutineScope.subscribingGetAsync( + coroutineContext: CoroutineContext = EmptyCoroutineContext, + timeoutMillis: Long = -1, + noinline filter: E.(E) -> R? +): Deferred = this.async(coroutineContext) { + subscribingGet(timeoutMillis, filter) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt index eeca3a407..9547437fd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscriber.kt @@ -101,6 +101,9 @@ interface Listener : CompletableJob { * * @param coroutineContext 给事件监听协程的额外的 [CoroutineContext] * + * @see subscribingGet 监听一个事件, 并尝试从这个事件中获取一个值. + * @see subscribingGetAsync 异步监听一个事件, 并尝试从这个事件中获取一个值. + * * @see subscribeAlways 一直监听 * @see subscribeOnce 只监听一次 *