From 4d6085c00666b09f63d303ea4056f3e5fffdd0d9 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 5 Apr 2020 16:01:33 +0800 Subject: [PATCH] Introduce `Message.contentToString` --- .../kotlin/net.mamoe.mirai/message/data/At.kt | 1 + .../net.mamoe.mirai/message/data/AtAll.kt | 1 + .../net.mamoe.mirai/message/data/Face.kt | 1 + .../message/data/HummerMessage.kt | 11 ++++-- .../net.mamoe.mirai/message/data/Image.kt | 1 + .../net.mamoe.mirai/message/data/Message.kt | 37 +++++++++++++++++-- .../message/data/MessageChain.kt | 22 +++++++---- .../message/data/MessageSource.kt | 26 +++++++++---- .../net.mamoe.mirai/message/data/PlainText.kt | 1 + .../message/data/QuoteReply.kt | 7 +++- .../message/data/RichMessage.kt | 2 + 11 files changed, 88 insertions(+), 22 deletions(-) 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 2848a51a5..0a5f6d91c 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 @@ -40,6 +40,7 @@ private constructor(val target: Long, val display: String) : constructor(member: Member) : this(member.id, "@${member.nameCardOrNick}") override fun toString(): String = "[mirai:at:$target]" + override fun contentToString(): String = this.display companion object Key : Message.Key { /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt index 81cb372ec..3357a5b51 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt @@ -36,6 +36,7 @@ object AtAll : @Suppress("SpellCheckingInspection") override fun toString(): String = "[mirai:atall]" + override fun contentToString(): String = "@全体成员" // 自动为消息补充 " " diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt index e985b7559..9f0ed43c5 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt @@ -25,6 +25,7 @@ class Face private constructor(val id: Int, private val stringValue: String) : constructor(id: Int) : this(id, "[mirai:face:$id]") override fun toString(): String = stringValue + override fun contentToString(): String = "[表情]" /** * @author LamGC diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt index d88527b62..76d36985b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/HummerMessage.kt @@ -81,6 +81,10 @@ class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常量") override fun subSequence(startIndex: Int, endIndex: Int): CharSequence = stringValue.subSequence(startIndex, endIndex) + override fun contentToString(): String { + return "[戳一戳]" + } + override fun compareTo(other: String): Int = stringValue.compareTo(other) //businessType=0x00000001(1) @@ -146,9 +150,10 @@ sealed class FlashImage : MessageContent, HummerMessage() { override fun get(index: Int) = stringValue!![index] override fun subSequence(startIndex: Int, endIndex: Int) = stringValue!!.subSequence(startIndex, endIndex) override fun compareTo(other: String) = other.compareTo(stringValue!!) + + override fun contentToString(): String = "[闪照]" } -@JvmSynthetic @SinceMirai("0.33.0") inline fun Image.flash(): FlashImage = FlashImage(this) @@ -164,7 +169,7 @@ inline fun FriendImage.flash(): FriendFlashImage = FlashImage(this) as FriendFla * @see FlashImage.invoke */ @SinceMirai("0.33.0") -class GroupFlashImage @MiraiInternalAPI constructor(override val image: GroupImage) : FlashImage() { +class GroupFlashImage(override val image: GroupImage) : FlashImage() { companion object Key : Message.Key } @@ -172,6 +177,6 @@ class GroupFlashImage @MiraiInternalAPI constructor(override val image: GroupIma * @see FlashImage.invoke */ @SinceMirai("0.33.0") -class FriendFlashImage @MiraiInternalAPI constructor(override val image: FriendImage) : FlashImage() { +class FriendFlashImage(override val image: FriendImage) : FlashImage() { companion object Key : Message.Key } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt index d19b9a144..20785d8d8 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt @@ -81,6 +81,7 @@ sealed class AbstractImage : Image { override fun compareTo(other: String): Int = _stringValue!!.compareTo(other) final override fun toString(): String = _stringValue!! + final override fun contentToString(): String = "[图片]" } // region 在线图片 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt index dc5bf6cdd..03dc6410d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt @@ -13,6 +13,9 @@ package net.mamoe.mirai.message.data import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.SinceMirai import kotlin.jvm.JvmSynthetic /** @@ -95,21 +98,38 @@ interface Message { * println(c) // "Hello world!" * ``` */ + @Suppress("DEPRECATION_ERROR") + @OptIn(MiraiInternalAPI::class) @JvmSynthetic // in java they should use `plus` instead fun followedBy(tail: Message): CombinedMessage { + if (this is ConstrainSingle<*> && tail is ConstrainSingle<*> + && this.key == tail.key + ) { + return CombinedMessage(EmptyMessageChain, this) + } return CombinedMessage(left = this, tail = tail) } /** - * 转换为易辨识的字符串. + * 得到包含 mirai 消息元素代码的, 易读的字符串. 如 `At(member) + "test"` 将转为 `"[mirai:at:qqId]test"` * * 各个 [SingleMessage] 的转换示例: * [PlainText]: "Hello" - * [GroupImage]: "[mirai:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]" - * [FriendImage]: "" + * [GroupImage]: "[mirai:image:{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.png]" + * [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]" + * [PokeMessage]: "[mirai:poke:1,-1]" + * [MessageChain]: 直接无间隔地连接所有元素. */ override fun toString(): String + /** + * 转为最接近官方格式的字符串. 如 `At(member) + "test"` 将转为 `"@群名片 test"`. + * 对于 [NullMessageChain], 这个函数返回空字符串 "" + * 对于其他 [MessageChain], 这个函数返回值同 [toString] + */ + @SinceMirai("0.34.0") + fun contentToString(): String + operator fun plus(another: Message): CombinedMessage = this.followedBy(another) // avoid resolution ambiguity @@ -147,6 +167,17 @@ interface MessageMetadata : SingleMessage { override fun compareTo(other: String): Int = "".compareTo(other) } +/** + * 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 但不会保持原顺序. + * + * **MiraiExperimentalAPI**: 此 API 可能在将来版本修改 + */ +@SinceMirai("0.34.0") +@MiraiExperimentalAPI +interface ConstrainSingle { + val key: Message.Key +} + /** * 消息内容 */ 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 bc569a0b2..457706a46 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 @@ -46,11 +46,6 @@ import kotlin.reflect.KProperty interface MessageChain : Message, Iterable { override operator fun contains(sub: String): Boolean - /** - * 得到易读的字符串 - */ - override fun toString(): String - /** * 元素数量 */ @@ -100,7 +95,7 @@ interface MessageChain : Message, Iterable { // region accessors /** - * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XmlMessage], [QuoteReply] + * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XmlMessage], [PokeMessage], [FlashImage] */ @JvmSynthetic inline fun MessageChain.foreachContent(block: (Message) -> Unit) { @@ -289,6 +284,7 @@ inline fun MessageChain.asMessageChain(): MessageChain = this // 避免套娃 @JvmSynthetic fun CombinedMessage.asMessageChain(): MessageChain { + @OptIn(MiraiExperimentalAPI::class) if (left is SingleMessage && this.tail is SingleMessage) { @Suppress("UNCHECKED_CAST") return (this as Iterable).asMessageChain() @@ -385,12 +381,13 @@ inline fun Sequence.flatten(): Sequence = this // fun Message.flatten(): Sequence { return when (this) { is MessageChain -> this.asSequence() - is CombinedMessage -> this.flatten() + is CombinedMessage -> this.flatten() // already constrained single. else -> sequenceOf(this as SingleMessage) } } fun CombinedMessage.flatten(): Sequence { + // already constrained single. if (this.isFlat()) { @Suppress("UNCHECKED_CAST") return (this as Iterable).asSequence() @@ -415,9 +412,13 @@ object EmptyMessageChain : MessageChain by MessageChainImplByCollection(emptyLis */ object NullMessageChain : MessageChain { override fun toString(): String = "NullMessageChain" + override fun contentToString(): String = "" override val size: Int get() = 0 override fun equals(other: Any?): Boolean = other === this override fun contains(sub: String): Boolean = error("accessing NullMessageChain") + + @OptIn(MiraiInternalAPI::class) + @Suppress("DEPRECATION_ERROR") override fun followedBy(tail: Message): CombinedMessage = CombinedMessage(left = EmptyMessageChain, tail = tail) override fun iterator(): MutableIterator = error("accessing NullMessageChain") } @@ -439,6 +440,8 @@ internal class MessageChainImplByIterable constructor( override fun toString(): String = toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it } + override fun contentToString(): String = toString() + override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) } } @@ -455,6 +458,8 @@ internal class MessageChainImplByCollection constructor( override fun toString(): String = toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it } + override fun contentToString(): String = toString() + override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) } } @@ -476,6 +481,8 @@ internal class MessageChainImplBySequence constructor( override fun toString(): String = toStringTemp ?: this.collected.joinToString("") { it.toString() }.also { toStringTemp = it } + override fun contentToString(): String = toString() + override operator fun contains(sub: String): Boolean = collected.any { it.contains(sub) } } @@ -488,6 +495,7 @@ internal class SingleMessageChainImpl constructor( ) : Message, Iterable, MessageChain { override val size: Int get() = 1 override fun toString(): String = this.delegate.toString() + override fun contentToString(): String = this.delegate.toString() override fun iterator(): Iterator = iterator { yield(delegate) } override operator fun contains(sub: String): Boolean = sub in delegate } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt index d71178fdd..71873a84e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt @@ -20,6 +20,7 @@ import net.mamoe.mirai.message.ContactMessage import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.recallIn import net.mamoe.mirai.utils.LazyProperty +import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.SinceMirai import kotlin.coroutines.CoroutineContext @@ -31,9 +32,7 @@ import kotlin.jvm.JvmSynthetic /** * 消息源, 它存在于 [MessageChain] 中, 用于表示这个消息的来源. * - * 消息源只用于 [引用回复][QuoteReply] 或 [撤回][Bot.recall]. - * - * `mirai-core-qqandroid`: `net.mamoe.mirai.qqandroid.message.MessageSourceFromMsg` + * 消息源可用于 [引用回复][QuoteReply] 或 [撤回][Bot.recall]. * * @see Bot.recall 撤回一条消息 * @see MessageSource.quote 引用这条消息, 创建 [MessageChain] @@ -56,17 +55,24 @@ sealed class MessageSource : Message, MessageMetadata { abstract val id: Int // random /** - * 发送时间, 单位为秒. 撤回好友消息时可能需要 + * 发送时间时间戳, 单位为秒. + * 撤回好友消息时需要 */ abstract val time: Int /** - * 发送人. 可能为机器人自己, 好友的 id, 或群 id + * 发送人. + * 当 [OnlineMessageSource.Outgoing] 时为 [机器人][Bot.id] + * 当 [OnlineMessageSource.Incoming] 时为发信 [目标好友][QQ.id] 或 [群][Group.id] + * 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] (取决于 [OfflineMessageSource.kind]) */ abstract val fromId: Long /** - * 发送目标. 可能为机器人自己, 好友的 id, 或群 id + * 发送目标. + * 当 [OnlineMessageSource.Outgoing] 时为发信 [目标好友][QQ.id] 或 [群][Group.id] + * 当 [OnlineMessageSource.Incoming] 时为 [机器人][Bot.id] + * 当 [OfflineMessageSource] 时为 [机器人][Bot.id], 发信 [目标好友][QQ.id] 或 [群][Group.id] (取决于 [OfflineMessageSource.kind]) */ abstract val targetId: Long // groupCode / friendUin @@ -76,7 +82,8 @@ sealed class MessageSource : Message, MessageMetadata { @LazyProperty abstract val originalMessage: MessageChain - final override fun toString(): String = "" + final override fun toString(): String = "[mirai:source:$id]" + final override fun contentToString(): String = "" } // ONLINE @@ -96,9 +103,12 @@ sealed class MessageSource : Message, MessageMetadata { * 当机器人接收一条消息 [ContactMessage], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源. */ @SinceMirai("0.33.0") -sealed class OnlineMessageSource : MessageSource() { +@OptIn(MiraiExperimentalAPI::class) +sealed class OnlineMessageSource : MessageSource(), ConstrainSingle { companion object Key : Message.Key + override val key: Message.Key get() = Key + /** * 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member]. * 即类型必定为 [Bot], [QQ] 或 [Member] diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt index 1b2f1f681..3ce2b2661 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt @@ -32,6 +32,7 @@ class PlainText(val stringValue: String) : override operator fun contains(sub: String): Boolean = sub in stringValue override fun toString(): String = stringValue + override fun contentToString(): String = stringValue override fun equals(other: Any?): Boolean { return other is PlainText && other.stringValue == this.stringValue diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt index d930ab2b0..6c6310ae1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt @@ -15,6 +15,7 @@ package net.mamoe.mirai.message.data import kotlinx.coroutines.Job import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.SinceMirai import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -30,12 +31,16 @@ import kotlin.jvm.JvmName * * @see MessageSource 获取更多信息 */ +@OptIn(MiraiExperimentalAPI::class) @SinceMirai("0.33.0") -class QuoteReply(val source: MessageSource) : Message, MessageMetadata { +class QuoteReply(val source: MessageSource) : Message, MessageMetadata, ConstrainSingle { // TODO: 2020/4/4 Metadata or Content? companion object Key : Message.Key + override val key: Message.Key get() = Key + override fun toString(): String = "[mirai:quote:${source.id}]" + override fun contentToString(): String = "" } suspend inline fun QuoteReply.recall() = this.source.recall() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/RichMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/RichMessage.kt index 0f834aebc..8574d641d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/RichMessage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/RichMessage.kt @@ -31,6 +31,8 @@ import kotlin.jvm.JvmSynthetic @SinceMirai("0.27.0") interface RichMessage : MessageContent { + override fun contentToString(): String = this.content + @SinceMirai("0.30.0") companion object Templates : Message.Key {