Add nextEvent, add docs

This commit is contained in:
Him188 2020-05-05 14:23:08 +08:00
parent 37ce266091
commit 96a5825283
3 changed files with 133 additions and 9 deletions

View File

@ -24,6 +24,8 @@ import kotlin.reflect.KClass
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see asyncFromEvent 本函数的异步版本
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*
* @throws TimeoutCancellationException 在超时后抛出.
* @throws Throwable [mapper] 抛出任何异常时, 本函数会抛出该异常
@ -55,6 +57,9 @@ suspend inline fun <reified E : Event, R : Any> syncFromEvent(
* @return 超时返回 `null`, 否则返回 [mapper] 返回的第一个非 `null` .
*
* @see asyncFromEvent 本函数的异步版本
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*
* @throws Throwable [mapper] 抛出任何异常时, 本函数会抛出该异常
*/
@JvmSynthetic
@ -77,6 +82,11 @@ suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param coroutineContext 额外的 [CoroutineContext]
* @param mapper 过滤转换器. 返回非 `null` 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
* @see asyncFromEvent
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*/
@JvmSynthetic
@Suppress("DeferredIsResult")
@ -100,6 +110,11 @@ inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull(
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param coroutineContext 额外的 [CoroutineContext]
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
* @see asyncFromEventOrNull
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*/
@JvmSynthetic
@Suppress("DeferredIsResult")
@ -127,9 +142,12 @@ internal suspend inline fun <E : Event, R> syncFromEventImpl(
crossinline mapper: suspend E.(E) -> R?
): R = suspendCancellableCoroutine { cont ->
coroutineScope.subscribe(eventClass) {
cont.resumeWith(kotlin.runCatching {
mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING
})
try {
cont.resumeWith(kotlin.runCatching {
mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING
})
} catch (e: Exception) {
}
return@subscribe ListeningStatus.STOPPED
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.event
import kotlinx.coroutines.*
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.BotEvent
import kotlin.coroutines.resume
import kotlin.jvm.JvmSynthetic
import kotlin.reflect.KClass
/**
* 挂起当前协程, 直到监听到事件 [E] 的广播, 返回这个事件实例.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制.
*
* @see subscribe 普通地监听一个事件
* @see syncFromEvent 挂起当前协程, 并尝试从事件中同步一个值
*
* @throws TimeoutCancellationException 在超时后抛出.
*/
@JvmSynthetic
suspend inline fun <reified E : Event> nextEvent(
timeoutMillis: Long = -1
): E {
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
return withTimeoutOrCoroutineScope(timeoutMillis) {
nextEventImpl(E::class, this)
}
}
/**
* 挂起当前协程, 直到监听到事件 [E] 的广播, 返回这个事件实例.
* 将筛选 [BotEvent.bot] [this] 相等的事件.
*
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制.
*
* @see subscribe 普通地监听一个事件
* @see syncFromEvent 挂起当前协程, 并尝试从事件中同步一个值
*
* @throws TimeoutCancellationException 在超时后抛出.
*/
@JvmSynthetic
suspend inline fun <reified E : BotEvent> Bot.nextEvent(
timeoutMillis: Long = -1
): E {
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
return withTimeoutOrCoroutineScope(timeoutMillis) {
nextBotEventImpl(this@nextEvent, E::class, this)
}
}
@JvmSynthetic
@PublishedApi
internal suspend inline fun <E : Event> nextEventImpl(
eventClass: KClass<E>,
coroutineScope: CoroutineScope
): E = suspendCancellableCoroutine { cont ->
coroutineScope.subscribe(eventClass) {
try {
cont.resume(this)
} catch (e: Exception) {
}
return@subscribe ListeningStatus.STOPPED
}
}
@JvmSynthetic
@PublishedApi
internal suspend inline fun <E : BotEvent> nextBotEventImpl(
bot: Bot,
eventClass: KClass<E>,
coroutineScope: CoroutineScope
): E = suspendCancellableCoroutine { cont ->
coroutineScope.subscribe(eventClass) {
try {
if (this.bot == bot) cont.resume(this)
} catch (e: Exception) {
}
return@subscribe ListeningStatus.STOPPED
}
}
@JvmSynthetic
@PublishedApi
internal suspend inline fun <R> withTimeoutOrCoroutineScope(
timeoutMillis: Long,
noinline block: suspend CoroutineScope.() -> R
): R {
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0 " }
return if (timeoutMillis == -1L) {
coroutineScope(block)
} else {
withTimeout(timeoutMillis, block)
}
}

View File

@ -446,7 +446,7 @@ class MessageSelectionTimeoutException : RuntimeException()
@JvmSynthetic
@PublishedApi
internal suspend inline fun <R> withTimeoutOrCoroutineScope(
internal suspend inline fun <R> withSilentTimeoutOrCoroutineScope(
timeoutMillis: Long,
noinline block: suspend CoroutineScope.() -> R
): R {
@ -480,7 +480,7 @@ internal suspend inline fun <reified T : MessageEvent, R> T.selectMessagesImpl(
filterContext: Boolean = true,
@BuilderInference
crossinline selectBuilder: @MessageDsl MessageSelectBuilderUnit<T, R>.() -> Unit
): R = withTimeoutOrCoroutineScope(timeoutMillis) {
): R = withSilentTimeoutOrCoroutineScope(timeoutMillis) {
var deferred: CompletableDeferred<R>? = CompletableDeferred()
coroutineContext[Job]!!.invokeOnCompletion {
deferred?.cancel()
@ -500,7 +500,7 @@ internal suspend inline fun <reified T : MessageEvent, R> T.selectMessagesImpl(
SELECT_MESSAGE_STUB,
outside
) {
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withTimeoutOrCoroutineScope
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withSilentTimeoutOrCoroutineScope
override fun obtainCurrentDeferred(): CompletableDeferred<R>? = deferred
override fun default(onEvent: MessageListener<T, R>) {
defaultListeners += onEvent
@ -516,7 +516,7 @@ internal suspend inline fun <reified T : MessageEvent, R> T.selectMessagesImpl(
SELECT_MESSAGE_STUB,
outside
) {
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withTimeoutOrCoroutineScope
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withSilentTimeoutOrCoroutineScope
override fun obtainCurrentDeferred(): CompletableDeferred<R>? = deferred
override fun default(onEvent: MessageListener<T, R>) {
defaultListeners += onEvent
@ -577,7 +577,7 @@ internal suspend inline fun <reified T : MessageEvent> T.whileSelectMessagesImpl
timeoutMillis: Long = -1,
filterContext: Boolean = true,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
) = withTimeoutOrCoroutineScope(timeoutMillis) {
) = withSilentTimeoutOrCoroutineScope(timeoutMillis) {
var deferred: CompletableDeferred<Boolean>? = CompletableDeferred()
coroutineContext[Job]!!.invokeOnCompletion {
deferred?.cancel()
@ -596,7 +596,7 @@ internal suspend inline fun <reified T : MessageEvent> T.whileSelectMessagesImpl
SELECT_MESSAGE_STUB,
outside
) {
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withTimeoutOrCoroutineScope
override fun obtainCurrentCoroutineScope(): CoroutineScope = this@withSilentTimeoutOrCoroutineScope
override fun obtainCurrentDeferred(): CompletableDeferred<Boolean>? = deferred
override fun default(onEvent: MessageListener<T, Boolean>) {
defaultListeners += onEvent