diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 9a97f643c..b37ba9d7d 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -45,6 +45,7 @@ fun ktor(id: String, version: String = Versions.ktor) = "io.ktor:ktor-$id:$versi val `kotlinx-coroutines-core` = kotlinx("coroutines-core", Versions.coroutines) +val `kotlinx-coroutines-jdk8` = kotlinx("coroutines-jdk8", Versions.coroutines) val `kotlinx-serialization-core` = kotlinx("serialization-core", Versions.serialization) val `kotlinx-serialization-json` = kotlinx("serialization-json", Versions.serialization) val `kotlinx-serialization-protobuf` = kotlinx("serialization-protobuf", Versions.serialization) diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index 896c36e0e..29ca771f7 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * 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. @@ -78,6 +78,7 @@ kotlin { api1(`kotlinx-io-jvm`) api1(`kotlinx-coroutines-io-jvm`) api(`kotlinx-coroutines-core`) + // api(`kotlinx-coroutines-jdk8`) implementation1(`kotlinx-atomicfu`) diff --git a/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt b/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt index 5d3bccef2..2a3d531b9 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * 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. @@ -11,12 +11,12 @@ package net.mamoe.mirai.message -import kotlinx.coroutines.Deferred import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.IMirai import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.* +import net.mamoe.mirai.message.action.AsyncRecallResult import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.MessageSource.Key.quote import net.mamoe.mirai.message.data.MessageSource.Key.recallIn @@ -70,7 +70,7 @@ public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor( * @see IMirai.recallMessage */ @Suppress("DeferredIsResult") - public fun recallIn(millis: Long): Deferred<Unit> = this.source.recallIn(millis) + public fun recallIn(millis: Long): AsyncRecallResult = this.source.recallIn(millis) /** * 引用这条消息. diff --git a/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt b/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt new file mode 100644 index 000000000..b3afffb69 --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt @@ -0,0 +1,90 @@ +/* + * 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 + */ + +@file:Suppress("MemberVisibilityCanBePrivate", "unused") + +package net.mamoe.mirai.message.action + +import kotlinx.coroutines.* +import net.mamoe.kjbb.JvmBlockingBridge +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.MessageSource.Key.recallIn +import java.util.concurrent.CompletableFuture + +/** + * [MessageSource.recallIn] 的结果. + * + * @see MessageSource.recallIn + */ +public class AsyncRecallResult internal constructor( + /** + * 撤回时产生的异常. Kotlin [Deferred] API. + */ + public val exception: Deferred<Throwable?>, +) { + /** + * 撤回时产生的异常. Java [CompletableFuture] API. + */ + public val exceptionFuture: CompletableFuture<Throwable?> by lazy { exception.asCompletableFuture() } + + /** + * 撤回是否成功. Kotlin [Deferred] API. + */ + public val isSuccess: Deferred<Boolean> by lazy { + GlobalScope.async { + kotlin.runCatching { exception.await() == null }.getOrElse { false } + } + } + + /** + * 撤回是否成功. Java [CompletableFuture] API. + */ + public val isSuccessFuture: CompletableFuture<Boolean> by lazy { isSuccess.asCompletableFuture() } + + /** + * 等待撤回完成, 返回撤回时产生的异常. + */ + @JvmBlockingBridge + public suspend fun awaitException(): Throwable? { + return exception.await() + } + + /** + * 等待撤回完成, 返回撤回的结果. + */ + @JvmBlockingBridge + public suspend fun awaitIsSuccess(): Boolean { + return isSuccess.await() + } +} + + +// copied from kotlinx-coroutines-jdk8 +private fun <T> Deferred<T>.asCompletableFuture(): CompletableFuture<T> { + val future = CompletableFuture<T>() + setupCancellation(future) + invokeOnCompletion { + @OptIn(ExperimentalCoroutinesApi::class) + try { + future.complete(getCompleted()) + } catch (t: Throwable) { + future.completeExceptionally(t) + } + } + return future +} + +// copied from kotlinx-coroutines-jdk8 +private fun Job.setupCancellation(future: CompletableFuture<*>) { + future.whenComplete { _, exception -> + cancel(exception?.let { + it as? CancellationException ?: CancellationException("CompletableFuture was completed exceptionally", it) + }) + } +} diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt index d5de174ee..f56b9f40e 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt @@ -25,6 +25,7 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.events.MessageEvent import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.action.AsyncRecallResult import net.mamoe.mirai.message.data.MessageSource.Key.quote import net.mamoe.mirai.message.data.MessageSource.Key.recall import net.mamoe.mirai.utils.LazyProperty @@ -199,20 +200,26 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle { */ @JvmStatic @Suppress("DeferredIsResult") - public fun MessageChain.recallIn(millis: Long): Deferred<Unit> = this.source.recallIn(millis) + public fun MessageChain.recallIn(millis: Long): AsyncRecallResult = this.source.recallIn(millis) /** * 在一段时间后撤回这条消息. * + * @return 返回撤回的结果 [Deferred]. [Deferred.await] 返回 `null` 表示成功执行 * @see IMirai.recallMessage */ @JvmStatic @Suppress("DeferredIsResult") - public fun MessageSource.recallIn(millis: Long): Deferred<Unit> { - return bot.async { - delay(millis) - Mirai.recallMessage(bot, this@recallIn) - } + public fun MessageSource.recallIn(millis: Long): AsyncRecallResult { + return AsyncRecallResult(bot.async { + try { + delay(millis) + Mirai.recallMessage(bot, this@recallIn) + null + } catch (e: Throwable) { + e + } + }) } /**