From 83faf81fb2a7bdfb72a7dd05ece0c60725dc8494 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 1 Mar 2020 15:26:46 +0800 Subject: [PATCH] Better API visibility --- .../mamoe/mirai/qqandroid/message/messages.kt | 32 +++- .../kotlin/net.mamoe.mirai/message/data/At.kt | 20 ++- .../message/data/MessageChain.kt | 154 +++++++++--------- 3 files changed, 119 insertions(+), 87 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index dbddf1bc9..58caab7ed 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.qqandroid.message import io.ktor.utils.io.core.* +import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.contact.Member import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody @@ -330,14 +331,13 @@ internal class NotOnlineImageFromServer( internal fun MsgComm.Msg.toMessageChain(): MessageChain { val elements = this.msgBody.richText.elems - val message = MessageChainBuilder(elements.size + 1) - message.add(MessageSourceFromMsg(delegate = this)) - elements.joinToMessageChain(message) - return message.asMessageChain() + return buildMessageChain(elements.size + 1) { + +MessageSourceFromMsg(delegate = this@toMessageChain) + elements.joinToMessageChain(this) + }.removeAtAfterQuoteReply().asMessageChain() } -// These two functions are not the same. - +// These two functions are not identical, dont combine. @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { val elements = this.elems!! @@ -345,11 +345,25 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { return buildMessageChain(elements.size + 1) { +MessageSourceFromServer(delegate = this@toMessageChain) elements.joinToMessageChain(this) + }.removeAtAfterQuoteReply().asMessageChain() +} + +private fun MessageChain.removeAtAfterQuoteReply(): List { + var last: Message? = null + return this.filterNot { message: Message -> + if (message is At) { // 筛除因 QuoteReply 导致的多余的 At + if (last != null || last !is QuoteReply) { + return@filterNot true + } + } else if (message is MessageContent) { + return@filterNot true + } + last = message + return@filterNot false } } - -@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class) +@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class) internal fun List.joinToMessageChain(message: MessageChainBuilder) { this.forEach { when { @@ -372,7 +386,7 @@ internal fun List.joinToMessageChain(message: MessageChainBuilde if (id == 0L) { message.add(AtAll) } else { - message.add(At(id, it.text.str)) + message.add(At._lowLevelConstructAtInstance(id, it.text.str)) } } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt index f2413eea3..c9204ab0d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt @@ -14,11 +14,12 @@ package net.mamoe.mirai.message.data +import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.nameCardOrNick -import net.mamoe.mirai.utils.MiraiInternalAPI import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic /** @@ -27,13 +28,24 @@ import kotlin.jvm.JvmName * @see AtAll 全体成员 */ class At -@MiraiInternalAPI constructor(val target: Long, val display: String) : Message, MessageContent { - @UseExperimental(MiraiInternalAPI::class) +private constructor(val target: Long, val display: String) : Message, MessageContent { + + /** + * 构造一个 [At] 实例. 这是唯一的公开的构造方式. + */ constructor(member: Member) : this(member.id, "@${member.nameCardOrNick}") override fun toString(): String = display - companion object Key : Message.Key + companion object Key : Message.Key { + /** + * 构造一个 [At], 仅供内部使用, 否则可能造成消息无法发出的问题. + */ + @Suppress("FunctionName") + @JvmStatic + @LowLevelAPI + fun _lowLevelConstructAtInstance(target: Long, display: String): At = At(target, display) + } // 自动为消息补充 " " diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt index f11dae7f2..81cbd3eb9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt @@ -9,12 +9,13 @@ @file:JvmMultifileClass @file:JvmName("MessageUtils") -@file:Suppress("unused") +@file:Suppress("unused", "NOTHING_TO_INLINE") package net.mamoe.mirai.message.data import net.mamoe.mirai.message.data.NullMessageChain.equals import net.mamoe.mirai.message.data.NullMessageChain.toString +import net.mamoe.mirai.utils.MiraiInternalAPI import kotlin.js.JsName import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -33,6 +34,8 @@ import kotlin.reflect.KProperty * @see toChain 将单个 [Message] 转换为 [MessageChain] * @see asMessageChain 将 [Iterable] 或 [Sequence] 委托为 [MessageChain] * + * @see foreachContent 遍历内容 + * * @see orNull 属性委托扩展 * @see orElse 属性委托扩展 * @see getValue 属性委托扩展 @@ -56,6 +59,18 @@ interface MessageChain : Message, Iterable { * @param key 由各个类型消息的伴生对象持有. 如 [PlainText.Key] */ fun getOrNull(key: Message.Key): M? = firstOrNull(key) + + /** + * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]. + * 仅供 `Java` 使用 + */ + @JsName("forEachContent") + @JvmName("forEachContent") + @Suppress("FunctionName") + @MiraiInternalAPI + fun `__forEachContent for Java__`(block: (Message) -> Unit) { + this.foreachContent(block) + } } // region accessors @@ -64,16 +79,8 @@ interface MessageChain : Message, Iterable { * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage] */ inline fun MessageChain.foreachContent(block: (Message) -> Unit) { - var last: Message? = null - this.forEach { message: Message -> - if (message is At) { // 筛除因 QuoteReply 导致的多余的 At - if (last != null || last !is QuoteReply) { - block(message) - } - } else if (message is MessageContent) { - block(message) - } - last = message + this.forEach { + if (it !is MessageMetadata) block(it) } } @@ -138,6 +145,7 @@ fun MessageChain.any(key: Message.Key): Boolean = firstOrNull(k * val at: At by message * val image: Image by message */ +@JvmSynthetic inline operator fun MessageChain.getValue(thisRef: Any?, property: KProperty<*>): T = this.first() /** @@ -161,6 +169,7 @@ inline class OrNullDelegate(private val value: Any?) { * @see orNull 提供一个不存在则 null 的委托 * @see orElse 提供一个不存在则使用默认值的委托 */ +@JvmSynthetic inline fun MessageChain.orNull(): OrNullDelegate = OrNullDelegate(this.firstOrNull()) /** @@ -175,6 +184,7 @@ inline fun MessageChain.orNull(): OrNullDelegate = OrN * ``` * @see orNull 提供一个不存在则 null 的委托 */ +@JvmSynthetic inline fun MessageChain.orElse( lazyDefault: () -> T ): OrNullDelegate = @@ -184,88 +194,61 @@ inline fun MessageChain.orElse( // region converters - -/** - * 返回 [EmptyMessageChain] - */ -@JvmName("newChain") -@JsName("newChain") -@Suppress("FunctionName") -fun MessageChain(): MessageChain = EmptyMessageChain - -/** - * 构造 [MessageChain] - * 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能 - */ -@JvmName("newChain") -@JsName("newChain") -@Suppress("FunctionName") -fun MessageChain(vararg messages: Message): MessageChain = - if (messages.isEmpty()) EmptyMessageChain - else MessageChainImplBySequence(messages.asSequence().flatten()) - -/** - * 构造 [MessageChain] 的快速途径 (无 [Array] 创建) - * 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能 - */ -@JvmName("newChain") -@JsName("newChain") -@Suppress("FunctionName") -fun MessageChain(message: Message): MessageChain = - when (message) { - is SingleMessage -> SingleMessageChainImpl(message) - else -> MessageChainImplBySequence(message.flatten()) - } - -/** - * 构造 [MessageChain] - */ -@JvmName("newChain") -@JsName("newChain") -@Suppress("FunctionName") -fun MessageChain(messages: Iterable): MessageChain = - MessageChainImplBySequence(messages.flatten()) - -/** - * [扁平化][flatten] [messages] 然后构造 [MessageChain] - */ -@JvmName("newChain") -@JsName("newChain") -@Suppress("FunctionName") -fun MessageChain(messages: List): MessageChain = - MessageChainImplByIterable(messages.flatten().asIterable()) - - /** * 得到包含 [this] 的 [MessageChain]. * * 若 [this] 为 [MessageChain] 将直接返回 this, * 若 [this] 为 [CombinedMessage] 将 [扁平化][flatten] 后委托为 [MessageChain], - * 否则将调用 [MessageChain] 构造一个 [MessageChainImplByIterable] + * 否则将调用 [asMessageChain] */ -@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") -@JvmSynthetic +@JvmName("newChain") +@JsName("newChain") +@Suppress("UNCHECKED_CAST") fun Message.toChain(): MessageChain = when (this) { is MessageChain -> this - is CombinedMessage -> MessageChainImplByIterable((this as Iterable).flatten().asIterable()) + is CombinedMessage -> (this as Iterable).asMessageChain() else -> SingleMessageChainImpl(this as SingleMessage) } /** * 直接将 [this] 委托为一个 [MessageChain] */ -@JvmName("asMessageChain1") @JvmSynthetic -@Suppress("unused", "NOTHING_TO_INLINE") +fun Collection.asMessageChain(): MessageChain = MessageChainImplByCollection(this) + +/** + * 将 [this] [扁平化后][flatten] 委托为一个 [MessageChain] + */ +@JvmName("newChain") +@JsName("newChain") +fun Collection.asMessageChain(): MessageChain = MessageChainImplBySequence(this.flatten()) + +/** + * 直接将 [this] 委托为一个 [MessageChain] + */ +@JvmSynthetic fun Iterable.asMessageChain(): MessageChain = MessageChainImplByIterable(this) /** * 将 [this] [扁平化后][flatten] 委托为一个 [MessageChain] */ -@JvmSynthetic -@Suppress("unused", "NOTHING_TO_INLINE") +@JvmName("newChain") +@JsName("newChain") fun Iterable.asMessageChain(): MessageChain = MessageChainImplBySequence(this.flatten()) +/** + * 直接将 [this] 委托为一个 [MessageChain] + */ +@JvmSynthetic +fun Sequence.asMessageChain(): MessageChain = MessageChainImplBySequence(this) + +/** + * 将 [this] [扁平化后][flatten] 委托为一个 [MessageChain] + */ +@JvmName("newChain") +@JsName("newChain") +fun Sequence.asMessageChain(): MessageChain = MessageChainImplBySequence(this.flatten()) + /** * 扁平化消息序列. * @@ -280,6 +263,9 @@ fun Iterable.asMessageChain(): MessageChain = MessageChainImplBySequenc */ fun Iterable.flatten(): Sequence = asSequence().flatten() +@JvmSynthetic +fun Iterable.flatten(): Sequence = this.asSequence() // fast path + /** * 扁平化消息序列. * @@ -294,6 +280,9 @@ fun Iterable.flatten(): Sequence = asSequence().flatten( */ fun Sequence.flatten(): Sequence = flatMap { it.flatten() } +@JvmSynthetic +fun Sequence.flatten(): Sequence = this // fast path + /** * 扁平化 [Message] * @@ -346,17 +335,34 @@ internal inline class MessageChainImplByIterable constructor( } /** - * 使用 [Iterable] 作为委托的 [MessageChain] + * 使用 [Collection] 作为委托的 [MessageChain] */ @PublishedApi -internal inline class MessageChainImplBySequence constructor( - private val delegate: Sequence +internal inline class MessageChainImplByCollection constructor( + private val delegate: Collection ) : Message, Iterable, MessageChain { override fun iterator(): Iterator = delegate.iterator() override fun toString(): String = this.delegate.joinToString("") { it.toString() } override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) } } +/** + * 使用 [Iterable] 作为委托的 [MessageChain] + */ +@PublishedApi +internal class MessageChainImplBySequence constructor( + delegate: Sequence +) : Message, Iterable, MessageChain { + /** + * [Sequence] 可能只能消耗一遍, 因此需要先转为 [List] + */ + private val collected: List by lazy { delegate.toList() } + + override fun iterator(): Iterator = collected.iterator() + override fun toString(): String = this.collected.joinToString("") { it.toString() } + override operator fun contains(sub: String): Boolean = collected.any { it.contains(sub) } +} + /** * 单个 [SingleMessage] 作为 [MessageChain] */