mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-19 11:49:17 +08:00
弃用无 receiver 的事件扩展函数, 增加其 EventChannel
receiver 版本替换 (#1754)
* Deprecate no-receiver functions `nextEvent*`, `syncFromEvent*`, `asyncFromEvent*`. Add their `EventChannel`-receiver counterparts. #1827 * Fix migration * Migrate `nextMessage` to new API
This commit is contained in:
parent
6f24035154
commit
701039ee48
@ -1789,6 +1789,11 @@ public final class net/mamoe/mirai/event/ExceptionInEventHandlerException : java
|
||||
public fun getMessage ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/event/ExtensionsKt {
|
||||
public static final fun nextEventImpl (Lnet/mamoe/mirai/event/EventChannel;Lkotlin/reflect/KClass;Lkotlinx/coroutines/CoroutineScope;Lnet/mamoe/mirai/event/EventPriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static final fun syncFromEventImpl (Lnet/mamoe/mirai/event/EventChannel;Lkotlin/reflect/KClass;Lkotlinx/coroutines/CoroutineScope;Lnet/mamoe/mirai/event/EventPriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/event/GlobalEventChannel : net/mamoe/mirai/event/EventChannel {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/event/GlobalEventChannel;
|
||||
}
|
||||
|
@ -1789,6 +1789,11 @@ public final class net/mamoe/mirai/event/ExceptionInEventHandlerException : java
|
||||
public fun getMessage ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/event/ExtensionsKt {
|
||||
public static final fun nextEventImpl (Lnet/mamoe/mirai/event/EventChannel;Lkotlin/reflect/KClass;Lkotlinx/coroutines/CoroutineScope;Lnet/mamoe/mirai/event/EventPriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static final fun syncFromEventImpl (Lnet/mamoe/mirai/event/EventChannel;Lkotlin/reflect/KClass;Lkotlinx/coroutines/CoroutineScope;Lnet/mamoe/mirai/event/EventPriority;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/event/GlobalEventChannel : net/mamoe/mirai/event/EventChannel {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/event/GlobalEventChannel;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public open class EventChannel<out BaseEvent : Event> @JvmOverloads internal con
|
||||
),
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
@DeprecatedSinceMirai(warningSince = "2.10.0-RC")
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@MiraiExperimentalApi
|
||||
public fun asChannel(
|
||||
capacity: Int = Channel.RENDEZVOUS,
|
||||
|
100
mirai-core-api/src/commonMain/kotlin/event/Extensions.kt
Normal file
100
mirai-core-api/src/commonMain/kotlin/event/Extensions.kt
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
/**
|
||||
* 挂起当前协程, 直到监听到事件 [E] 的广播并通过 [filter], 返回这个事件实例.
|
||||
*
|
||||
* @param filter 过滤器. 返回 `true` 时表示得到了需要的实例. 返回 `false` 时表示继续监听
|
||||
*
|
||||
* @see EventChannel.subscribe 普通地监听一个事件
|
||||
* @see EventChannel.syncFromEvent 挂起当前协程, 并尝试从事件中同步一个值
|
||||
*
|
||||
* @since 2.10
|
||||
*/
|
||||
public suspend inline fun <reified E : Event> EventChannel<*>.nextEvent(
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
noinline filter: suspend (E) -> Boolean = { true }
|
||||
): E = coroutineScope { this@nextEvent.nextEventImpl(E::class, this@coroutineScope, priority, filter) }
|
||||
|
||||
/**
|
||||
* 挂起当前协程, 监听事件 [E], 并尝试从这个事件中**获取**一个值, 在超时时抛出 [TimeoutCancellationException]
|
||||
*
|
||||
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
|
||||
*
|
||||
* @see asyncFromEvent 本函数的异步版本
|
||||
* @see EventChannel.subscribe 普通地监听一个事件
|
||||
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
|
||||
*
|
||||
* @see syncFromEventOrNull 本函数的在超时后返回 `null` 的版本
|
||||
*
|
||||
* @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
|
||||
*
|
||||
* @since 2.10
|
||||
*/
|
||||
public suspend inline fun <reified E : Event, R : Any> EventChannel<*>.syncFromEvent(
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
noinline mapper: suspend (E) -> R?
|
||||
): R = coroutineScope { this@syncFromEvent.syncFromEventImpl(E::class, this, priority, mapper) }
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.10
|
||||
*/
|
||||
@PublishedApi
|
||||
internal suspend fun <E : Event> EventChannel<Event>.nextEventImpl(
|
||||
eventClass: KClass<E>,
|
||||
coroutineScope: CoroutineScope,
|
||||
priority: EventPriority,
|
||||
filter: suspend (E) -> Boolean
|
||||
): E = suspendCancellableCoroutine { cont ->
|
||||
var listener: Listener<E>? = null
|
||||
listener = parentScope(coroutineScope)
|
||||
.subscribe(eventClass, priority = priority) { event ->
|
||||
if (!filter(event)) return@subscribe ListeningStatus.LISTENING
|
||||
|
||||
try {
|
||||
cont.resume(event)
|
||||
} finally {
|
||||
listener?.complete() // ensure completed on exceptions
|
||||
}
|
||||
return@subscribe ListeningStatus.STOPPED
|
||||
}
|
||||
|
||||
cont.invokeOnCancellation {
|
||||
runCatching { listener.cancel("nextEvent outer scope cancelled", it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.10
|
||||
*/
|
||||
@PublishedApi
|
||||
internal suspend fun <E : Event, R> EventChannel<*>.syncFromEventImpl(
|
||||
eventClass: KClass<E>,
|
||||
coroutineScope: CoroutineScope,
|
||||
priority: EventPriority,
|
||||
mapper: suspend (E) -> R?
|
||||
): R = suspendCancellableCoroutine { cont ->
|
||||
parentScope(coroutineScope).subscribe(eventClass, priority = priority) { event ->
|
||||
try {
|
||||
cont.resumeWith(kotlin.runCatching {
|
||||
mapper.invoke(event) ?: return@subscribe ListeningStatus.LISTENING
|
||||
})
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return@subscribe ListeningStatus.STOPPED
|
||||
}
|
||||
}
|
@ -8,12 +8,14 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@file:JvmName("NextEventKt")
|
||||
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.event.events.BotEvent
|
||||
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ -21,6 +23,11 @@ import kotlin.reflect.KClass
|
||||
/**
|
||||
* 挂起当前协程, 直到监听到事件 [E] 的广播并通过 [filter], 返回这个事件实例.
|
||||
*
|
||||
* ### 已弃用
|
||||
*
|
||||
* 该函数相当于 [GlobalEventChannel.nextEvent].
|
||||
* 不一定需要将所有被弃用的 [nextEvent] 都换成 `GlobalEventChannel.nextEvent`, 请根据情况选择合适的 [EventChannel].
|
||||
*
|
||||
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制.
|
||||
* @param filter 过滤器. 返回 `true` 时表示得到了需要的实例. 返回 `false` 时表示继续监听
|
||||
*
|
||||
@ -30,21 +37,34 @@ import kotlin.reflect.KClass
|
||||
* @throws TimeoutCancellationException 在超时后抛出.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@Deprecated(
|
||||
"Use GlobalEventChannel.nextEvent",
|
||||
ReplaceWith(
|
||||
"if (timeoutMillis == -1L) { GlobalEventChannel.nextEvent<E>(priority, filter) } else { withTimeout(timeoutMillis) { GlobalEventChannel.nextEvent<E>(priority, filter) } }",
|
||||
"net.mamoe.mirai.event.GlobalEventChannel",
|
||||
"kotlinx.coroutines.withTimeout",
|
||||
),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
public suspend inline fun <reified E : Event> nextEvent(
|
||||
timeoutMillis: Long = -1,
|
||||
priority: EventPriority = EventPriority.MONITOR,
|
||||
crossinline filter: (E) -> Boolean = { true }
|
||||
): E {
|
||||
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
|
||||
return withTimeoutOrCoroutineScope(timeoutMillis) {
|
||||
nextEventImpl(E::class, this, priority, filter)
|
||||
}
|
||||
): E = if (timeoutMillis == -1L) {
|
||||
GlobalEventChannel.nextEvent(priority) { filter(it) }
|
||||
} else {
|
||||
withTimeout(timeoutMillis) { GlobalEventChannel.nextEvent(priority) { filter(it) } }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 挂起当前协程, 直到监听到事件 [E] 的广播并通过 [filter], 返回这个事件实例.
|
||||
*
|
||||
* ### 已弃用
|
||||
*
|
||||
* 该函数相当于 [GlobalEventChannel.nextEvent].
|
||||
* 不一定需要将所有被弃用的 [nextEvent] 都换成 `GlobalEventChannel.nextEvent`, 请根据情况选择合适的 [EventChannel].
|
||||
*
|
||||
* @param timeoutMillis 超时. 单位为毫秒.
|
||||
* @param filter 过滤器. 返回 `true` 时表示得到了需要的实例. 返回 `false` 时表示继续监听
|
||||
*
|
||||
@ -54,19 +74,37 @@ public suspend inline fun <reified E : Event> nextEvent(
|
||||
* @return 事件实例, 在超时后返回 `null`
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@Deprecated(
|
||||
"Use GlobalEventChannel.nextEvent",
|
||||
ReplaceWith(
|
||||
"withTimeoutOrNull(timeoutMillis) { GlobalEventChannel.nextEvent<E>(priority, filter) }",
|
||||
|
||||
"kotlinx.coroutines.withTimeoutOrNull",
|
||||
"net.mamoe.mirai.event.GlobalEventChannel",
|
||||
"net.mamoe.mirai.event.nextEvent"
|
||||
),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
public suspend inline fun <reified E : Event> nextEventOrNull(
|
||||
timeoutMillis: Long,
|
||||
priority: EventPriority = EventPriority.MONITOR,
|
||||
crossinline filter: (E) -> Boolean = { true }
|
||||
): E? {
|
||||
return withTimeoutOrNull(timeoutMillis) {
|
||||
nextEventImpl(E::class, this, priority, filter)
|
||||
}
|
||||
}
|
||||
): E? = withTimeoutOrNull(timeoutMillis) { GlobalEventChannel.nextEvent(priority) { filter(it) } }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// internals
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@PublishedApi
|
||||
@Deprecated("Kept for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@DeprecatedSinceMirai(hiddenSince = "2.10")
|
||||
internal suspend inline fun <E : Event> nextEventImpl(
|
||||
eventClass: KClass<E>,
|
||||
coroutineScope: CoroutineScope,
|
||||
@ -80,7 +118,7 @@ internal suspend inline fun <E : Event> nextEventImpl(
|
||||
|
||||
try {
|
||||
cont.resume(this)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return@subscribe ListeningStatus.STOPPED
|
||||
}
|
||||
@ -98,6 +136,7 @@ internal inline fun <BaseEvent : Event> EventChannel<BaseEvent>.parentJob(job: J
|
||||
|
||||
@JvmSynthetic
|
||||
@PublishedApi
|
||||
@Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
internal suspend inline fun <E : BotEvent> nextBotEventImpl(
|
||||
bot: Bot,
|
||||
eventClass: KClass<E>,
|
||||
@ -109,7 +148,7 @@ internal suspend inline fun <E : BotEvent> nextBotEventImpl(
|
||||
.subscribe(eventClass, priority = priority) {
|
||||
try {
|
||||
if (this.bot == bot) cont.resume(this)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return@subscribe ListeningStatus.STOPPED
|
||||
}
|
||||
@ -121,10 +160,12 @@ internal suspend inline fun <E : BotEvent> nextBotEventImpl(
|
||||
|
||||
@JvmSynthetic
|
||||
@PublishedApi
|
||||
internal suspend inline fun <R> withTimeoutOrCoroutineScope(
|
||||
@Deprecated("Kept for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@DeprecatedSinceMirai(hiddenSince = "2.10")
|
||||
internal suspend fun <R> withTimeoutOrCoroutineScope(
|
||||
timeoutMillis: Long,
|
||||
useCoroutineScope: CoroutineScope? = null,
|
||||
noinline block: suspend CoroutineScope.() -> R
|
||||
block: suspend CoroutineScope.() -> R
|
||||
): R {
|
||||
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0 " }
|
||||
|
||||
@ -138,7 +179,8 @@ internal suspend inline fun <R> withTimeoutOrCoroutineScope(
|
||||
|
||||
@JvmSynthetic
|
||||
@PublishedApi
|
||||
@Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@Deprecated("Kept for binary compatibility", level = DeprecationLevel.HIDDEN)
|
||||
@DeprecatedSinceMirai(hiddenSince = "2.10")
|
||||
internal suspend inline fun <R> withTimeoutOrCoroutineScope(
|
||||
timeoutMillis: Long,
|
||||
noinline block: suspend CoroutineScope.() -> R
|
@ -8,10 +8,12 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@file:JvmName("NextEventAsyncKt")
|
||||
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@ -32,21 +34,44 @@ import kotlin.coroutines.EmptyCoroutineContext
|
||||
* @since 2.2
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@Deprecated(
|
||||
"Please use `async` with `nextEvent` manually.",
|
||||
ReplaceWith(
|
||||
"""async(coroutineContext) {
|
||||
if (timeoutMillis == -1L) {
|
||||
this.globalEventChannel(coroutineContext).nextEvent<E>(priority, filter)
|
||||
} else {
|
||||
withTimeout(timeoutMillis) {
|
||||
GlobalEventChannel.nextEvent<E>(priority, filter)
|
||||
}
|
||||
}
|
||||
}""",
|
||||
"kotlinx.coroutines.async",
|
||||
"kotlinx.coroutines.withTimeout",
|
||||
"net.mamoe.mirai.event.globalEventChannel",
|
||||
"net.mamoe.mirai.event.GlobalEventChannel",
|
||||
"net.mamoe.mirai.event.nextEvent"
|
||||
),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@MiraiExperimentalApi
|
||||
public inline fun <reified E : Event> CoroutineScope.nextEventAsync(
|
||||
timeoutMillis: Long = -1,
|
||||
priority: EventPriority = EventPriority.MONITOR,
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
crossinline filter: (E) -> Boolean = { true }
|
||||
): Deferred<E> {
|
||||
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
|
||||
return async(coroutineContext) {
|
||||
withTimeoutOrCoroutineScope(timeoutMillis, this) {
|
||||
nextEventImpl(E::class, this, priority, filter)
|
||||
): Deferred<E> =
|
||||
async(coroutineContext) {
|
||||
val block: suspend (E) -> Boolean = { filter(it) } // inline once.
|
||||
if (timeoutMillis == -1L) {
|
||||
this.globalEventChannel(coroutineContext).nextEvent(priority, block)
|
||||
} else {
|
||||
withTimeout(timeoutMillis) {
|
||||
GlobalEventChannel.nextEvent(priority, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回一个 [Deferred], 其值为下一个广播并通过 [filter] 的事件 [E] 示例.
|
||||
@ -63,6 +88,27 @@ public inline fun <reified E : Event> CoroutineScope.nextEventAsync(
|
||||
* @since 2.2
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
@Deprecated(
|
||||
"Please use `async` with `nextEventOrNull` manually.",
|
||||
ReplaceWith(
|
||||
"""async(coroutineContext) {
|
||||
if (timeoutMillis == -1L) {
|
||||
this.globalEventChannel(coroutineContext).nextEvent<E>(priority, filter)
|
||||
} else {
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
GlobalEventChannel.nextEvent<E>(priority, filter)
|
||||
}
|
||||
}
|
||||
}""",
|
||||
"kotlinx.coroutines.async",
|
||||
"kotlinx.coroutines.withTimeoutOrNull",
|
||||
"net.mamoe.mirai.event.globalEventChannel",
|
||||
"net.mamoe.mirai.event.GlobalEventChannel",
|
||||
"net.mamoe.mirai.event.nextEvent"
|
||||
),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@JvmSynthetic
|
||||
public inline fun <reified E : Event> CoroutineScope.nextEventOrNullAsync(
|
||||
timeoutMillis: Long,
|
||||
@ -71,8 +117,13 @@ public inline fun <reified E : Event> CoroutineScope.nextEventOrNullAsync(
|
||||
crossinline filter: (E) -> Boolean = { true }
|
||||
): Deferred<E?> {
|
||||
return async(coroutineContext) {
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
nextEventImpl(E::class, this, priority, filter)
|
||||
val block: suspend (E) -> Boolean = { filter(it) } // inline once.
|
||||
if (timeoutMillis == -1L) {
|
||||
this.globalEventChannel(coroutineContext).nextEvent(priority, block)
|
||||
} else {
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
GlobalEventChannel.nextEvent(priority, block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,16 +8,18 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
@file:JvmName("SyncFromEventKt")
|
||||
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* 挂起当前协程, 监听事件 [E], 并尝试从这个事件中**同步**一个值, 在超时时抛出 [TimeoutCancellationException]
|
||||
* 挂起当前协程, 监听事件 [E], 并尝试从这个事件中**获取**一个值, 在超时时抛出 [TimeoutCancellationException]
|
||||
*
|
||||
* @param timeoutMillis 超时. 单位为毫秒.
|
||||
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
|
||||
@ -31,6 +33,17 @@ import kotlin.reflect.KClass
|
||||
* @throws TimeoutCancellationException 在超时后抛出.
|
||||
* @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
|
||||
*/
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@Deprecated(
|
||||
"Use GlobalEventChannel.syncFromEvent",
|
||||
ReplaceWith(
|
||||
"if (timeoutMillis == -1L) { GlobalEventChannel.syncFromEvent<E, R>(priority) { mapper.invoke(it, it) } } else { withTimeout(timeoutMillis) { GlobalEventChannel.syncFromEvent<E, R>(priority) { mapper.invoke(it, it) } } }",
|
||||
"kotlinx.coroutines.withTimeout",
|
||||
"net.mamoe.mirai.event.GlobalEventChannel",
|
||||
"net.mamoe.mirai.event.syncFromEvent"
|
||||
),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@JvmSynthetic
|
||||
public suspend inline fun <reified E : Event, R : Any> syncFromEvent(
|
||||
timeoutMillis: Long = -1,
|
||||
@ -41,11 +54,11 @@ public suspend inline fun <reified E : Event, R : Any> syncFromEvent(
|
||||
|
||||
return if (timeoutMillis == -1L) {
|
||||
coroutineScope {
|
||||
syncFromEventImpl<E, R>(E::class, this, priority, mapper)
|
||||
GlobalEventChannel.syncFromEventImpl(E::class, this, priority) { mapper.invoke(it, it) }
|
||||
}
|
||||
} else {
|
||||
withTimeout(timeoutMillis) {
|
||||
syncFromEventImpl<E, R>(E::class, this, priority, mapper)
|
||||
GlobalEventChannel.syncFromEventImpl(E::class, this, priority) { mapper.invoke(it, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,6 +78,12 @@ public suspend inline fun <reified E : Event, R : Any> syncFromEvent(
|
||||
* @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
|
||||
*/
|
||||
@JvmSynthetic
|
||||
@DeprecatedSinceMirai(warningSince = "2.10")
|
||||
@Deprecated(
|
||||
"Use GlobalEventChannel.syncFromEvent",
|
||||
ReplaceWith("withTimeoutOrNull(timeoutMillis) { GlobalEventChannel.syncFromEvent<E, R>(priority) { event -> with(event) { mapper(event) } }"),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
public suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
|
||||
timeoutMillis: Long,
|
||||
priority: EventPriority = EventPriority.MONITOR,
|
||||
@ -73,7 +92,7 @@ public suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
|
||||
require(timeoutMillis > 0) { "timeoutMillis must be > 0" }
|
||||
|
||||
return withTimeoutOrNull(timeoutMillis) {
|
||||
syncFromEventImpl<E, R>(E::class, this, priority, mapper)
|
||||
GlobalEventChannel.syncFromEventImpl(E::class, this, priority) { mapper.invoke(it, it) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +110,20 @@ public suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
|
||||
* @see EventChannel.subscribe 普通地监听一个事件
|
||||
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
|
||||
*/
|
||||
@Deprecated(
|
||||
"Please use `async` and `syncFromEvent` manually.",
|
||||
replaceWith = ReplaceWith(
|
||||
"""async(coroutineContext) {
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
GlobalEventChannel.syncFromEvent<E, R>(priority, filter)
|
||||
}
|
||||
}""",
|
||||
"kotlinx.coroutines.async",
|
||||
"kotlinx.coroutines.withTimeoutOrNull",
|
||||
"net.mamoe.mirai.event.globalEventChannel",
|
||||
"net.mamoe.mirai.event.syncFromEvent"
|
||||
),
|
||||
)
|
||||
@JvmSynthetic
|
||||
@Suppress("DeferredIsResult")
|
||||
public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull(
|
||||
@ -101,11 +134,14 @@ public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNu
|
||||
): Deferred<R?> {
|
||||
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
|
||||
return this.async(coroutineContext) {
|
||||
syncFromEventOrNull(timeoutMillis, priority, mapper)
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
GlobalEventChannel.syncFromEvent<E, R>(priority) { event ->
|
||||
mapper(event, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 异步监听这个事件, 并尝试从这个事件中获取一个值.
|
||||
*
|
||||
@ -120,6 +156,25 @@ public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNu
|
||||
* @see EventChannel.subscribe 普通地监听一个事件
|
||||
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
|
||||
*/
|
||||
@Deprecated(
|
||||
"Please use `async` and `syncFromEvent` manually.",
|
||||
replaceWith = ReplaceWith(
|
||||
"""async(coroutineContext) {
|
||||
if (timeoutMillis == -1L) {
|
||||
this.globalEventChannel(coroutineContext).syncFromEvent<E, R>(priority, filter)
|
||||
} else {
|
||||
withTimeout(timeoutMillis) {
|
||||
GlobalEventChannel.syncFromEvent<E, R>(priority, filter)
|
||||
}
|
||||
}
|
||||
}""",
|
||||
"kotlinx.coroutines.async",
|
||||
"kotlinx.coroutines.withTimeout",
|
||||
"net.mamoe.mirai.event.globalEventChannel",
|
||||
"net.mamoe.mirai.event.syncFromEvent"
|
||||
),
|
||||
)
|
||||
@DeprecatedSinceMirai("2.10")
|
||||
@JvmSynthetic
|
||||
@Suppress("DeferredIsResult")
|
||||
public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent(
|
||||
@ -130,7 +185,11 @@ public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent(
|
||||
): Deferred<R> {
|
||||
require(timeoutMillis == -1L || timeoutMillis > 0) { "timeoutMillis must be -1 or > 0" }
|
||||
return this.async(coroutineContext) {
|
||||
syncFromEvent(timeoutMillis, priority, mapper)
|
||||
if (timeoutMillis == -1L) {
|
||||
GlobalEventChannel.syncFromEvent(priority) { it: E -> mapper.invoke(it, it) }
|
||||
} else {
|
||||
withTimeout(timeoutMillis) { GlobalEventChannel.syncFromEvent(priority) { it: E -> mapper.invoke(it, it) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,6 +198,8 @@ public inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent(
|
||||
//// internal
|
||||
//////////////
|
||||
|
||||
@Deprecated("Deprecated since its usages are deprecated")
|
||||
@DeprecatedSinceMirai("2.10")
|
||||
@JvmSynthetic
|
||||
@PublishedApi
|
||||
internal suspend inline fun <E : Event, R> syncFromEventImpl(
|
||||
@ -152,7 +213,7 @@ internal suspend inline fun <E : Event, R> syncFromEventImpl(
|
||||
cont.resumeWith(kotlin.runCatching {
|
||||
mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return@subscribe ListeningStatus.STOPPED
|
||||
}
|
@ -15,8 +15,11 @@ package net.mamoe.mirai.message
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import net.mamoe.mirai.event.EventPriority
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.event.GlobalEventChannel
|
||||
import net.mamoe.mirai.event.events.MessageEvent
|
||||
import net.mamoe.mirai.event.syncFromEvent
|
||||
import net.mamoe.mirai.event.syncFromEventOrNull
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
@ -48,9 +51,15 @@ public suspend inline fun <reified P : MessageEvent> P.nextMessage(
|
||||
priority: EventPriority = EventPriority.MONITOR,
|
||||
noinline filter: suspend P.(P) -> Boolean = { true }
|
||||
): MessageChain {
|
||||
return syncFromEvent<P, P>(timeoutMillis, priority) {
|
||||
takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) }
|
||||
}.message
|
||||
val mapper: suspend (P) -> P? = createMapper(filter)
|
||||
|
||||
return (if (timeoutMillis == -1L) {
|
||||
GlobalEventChannel.syncFromEvent(priority, mapper)
|
||||
} else {
|
||||
withTimeout(timeoutMillis) {
|
||||
GlobalEventChannel.syncFromEvent(priority, mapper)
|
||||
}
|
||||
}).message
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,11 +80,29 @@ public suspend inline fun <reified P : MessageEvent> P.nextMessageOrNull(
|
||||
noinline filter: suspend P.(P) -> Boolean = { true }
|
||||
): MessageChain? {
|
||||
require(timeoutMillis > 0) { "timeoutMillis must be > 0" }
|
||||
return syncFromEventOrNull<P, P>(timeoutMillis, priority) {
|
||||
takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }?.takeIf { filter(it, it) }
|
||||
}?.message
|
||||
|
||||
val mapper: suspend (P) -> P? = createMapper(filter)
|
||||
|
||||
return (if (timeoutMillis == -1L) {
|
||||
GlobalEventChannel.syncFromEvent(priority, mapper)
|
||||
} else {
|
||||
withTimeoutOrNull(timeoutMillis) {
|
||||
GlobalEventChannel.syncFromEvent(priority, mapper)
|
||||
}
|
||||
})?.message
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.10
|
||||
*/
|
||||
@PublishedApi // inline, safe to remove in the future
|
||||
internal inline fun <reified P : MessageEvent> P.createMapper(crossinline filter: suspend P.(P) -> Boolean): suspend (P) -> P? =
|
||||
mapper@{ event ->
|
||||
if (!event.isContextIdenticalWith(this)) return@mapper null
|
||||
if (!filter(event, event)) return@mapper null
|
||||
event
|
||||
}
|
||||
|
||||
/**
|
||||
* @see nextMessage
|
||||
*/
|
||||
|
173
mirai-core-api/src/jvmTest/kotlin/event/NextEventTest.kt
Normal file
173
mirai-core-api/src/jvmTest/kotlin/event/NextEventTest.kt
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package net.mamoe.mirai.event
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@JvmBlockingBridge
|
||||
internal class NextEventTest : AbstractEventTest() {
|
||||
private val dispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
|
||||
|
||||
@AfterEach
|
||||
fun stopDispatcher() {
|
||||
dispatcher.close()
|
||||
}
|
||||
|
||||
|
||||
data class TE(
|
||||
val x: Int
|
||||
) : AbstractEvent()
|
||||
|
||||
data class TE2(
|
||||
val x: Int
|
||||
) : AbstractEvent()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// nextEvent
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
suspend fun `nextEvent can receive`() {
|
||||
val channel = GlobalEventChannel
|
||||
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
channel.nextEvent<TE>()
|
||||
}
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(1), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEvent can filter type`() {
|
||||
val channel = GlobalEventChannel
|
||||
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
channel.nextEvent<TE>()
|
||||
}
|
||||
|
||||
TE2(1).broadcast()
|
||||
yield()
|
||||
assertFalse { deferred.isCompleted }
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(1), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEvent can filter by filter`() {
|
||||
val channel = GlobalEventChannel
|
||||
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
channel.nextEvent<TE> { it.x == 2 }
|
||||
}
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertFalse { deferred.isCompleted }
|
||||
|
||||
TE(2).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(2), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEvent can timeout`() {
|
||||
val channel = GlobalEventChannel
|
||||
|
||||
withContext(dispatcher) {
|
||||
assertThrows<TimeoutCancellationException> {
|
||||
withTimeout(timeMillis = 1) { channel.nextEvent<TE>(EventPriority.MONITOR) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// nextEventOrNull
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Test
|
||||
suspend fun `nextEventOrNull can receive`() {
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
nextEventOrNull<TE>(5000)
|
||||
}
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(1), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEventOrNull can filter type`() {
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
nextEventOrNull<TE>(5000)
|
||||
}
|
||||
|
||||
TE2(1).broadcast()
|
||||
yield()
|
||||
assertFalse { deferred.isCompleted }
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(1), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEventOrNull can filter by filter`() {
|
||||
withContext(dispatcher) {
|
||||
val deferred = async(start = CoroutineStart.UNDISPATCHED) {
|
||||
nextEventOrNull<TE>(5000) { it.x == 2 }
|
||||
}
|
||||
|
||||
TE(1).broadcast()
|
||||
yield()
|
||||
assertFalse { deferred.isCompleted }
|
||||
|
||||
TE(2).broadcast()
|
||||
yield()
|
||||
assertTrue { deferred.isCompleted }
|
||||
assertEquals(TE(2), deferred.await())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
suspend fun `nextEventOrNull can timeout`() {
|
||||
withContext(dispatcher) {
|
||||
assertEquals(null, nextEventOrNull<TE>(timeoutMillis = 1))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user