1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-15 07:37:08 +08:00

Implement ConstrainSingle

This commit is contained in:
Him188 2020-04-05 17:18:23 +08:00
parent 4d6085c006
commit 3714b1b95e
12 changed files with 289 additions and 63 deletions
mirai-core/src

View File

@ -43,6 +43,9 @@ private constructor(val target: Long, val display: String) :
override fun contentToString(): String = this.display
companion object Key : Message.Key<At> {
override val typeName: String
get() = "At"
/**
* 构造一个 [At], 仅供内部使用, 否则可能造成消息无法发出的问题.
*/

View File

@ -33,6 +33,8 @@ object AtAll :
@SinceMirai("0.31.2")
const val display = displayA
override val typeName: String
get() = "AtAll"
@Suppress("SpellCheckingInspection")
override fun toString(): String = "[mirai:atall]"

View File

@ -32,6 +32,9 @@ class Face private constructor(val id: Int, private val stringValue: String) :
*/
@Suppress("SpellCheckingInspection", "unused")
companion object IdList : Message.Key<Face> {
override val typeName: String
get() = "Face"
const val unknown: Int = 0xff
const val jingya: Int = 0
const val piezui: Int = 1

View File

@ -27,7 +27,10 @@ import kotlin.jvm.JvmSynthetic
*/
@SinceMirai("0.31.0")
sealed class HummerMessage : MessageContent {
companion object Key : Message.Key<HummerMessage>
companion object Key : Message.Key<HummerMessage> {
override val typeName: String
get() = "HummerMessage"
}
// has service type etc.
}
@ -47,6 +50,9 @@ class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常量")
val id: Int
) : HummerMessage() {
companion object Types : Message.Key<PokeMessage> {
override val typeName: String
get() = "PokeMessage"
/** 戳一戳 */
@JvmField
val Poke = PokeMessage(1, -1)
@ -130,6 +136,9 @@ sealed class FlashImage : MessageContent, HummerMessage() {
operator fun invoke(imageId: String): FlashImage {
return invoke(Image(imageId))
}
override val typeName: String
get() = "FlashImage"
}
/**
@ -170,7 +179,10 @@ inline fun FriendImage.flash(): FriendFlashImage = FlashImage(this) as FriendFla
*/
@SinceMirai("0.33.0")
class GroupFlashImage(override val image: GroupImage) : FlashImage() {
companion object Key : Message.Key<GroupFlashImage>
companion object Key : Message.Key<GroupFlashImage> {
override val typeName: String
get() = "GroupFlashImage"
}
}
/**
@ -178,5 +190,8 @@ class GroupFlashImage(override val image: GroupImage) : FlashImage() {
*/
@SinceMirai("0.33.0")
class FriendFlashImage(override val image: FriendImage) : FlashImage() {
companion object Key : Message.Key<FriendFlashImage>
companion object Key : Message.Key<FriendFlashImage> {
override val typeName: String
get() = "FriendFlashImage"
}
}

View File

@ -33,7 +33,10 @@ import kotlin.jvm.JvmSynthetic
* @see Image.flash 转换普通图片为闪照
*/
interface Image : Message, MessageContent {
companion object Key : Message.Key<Image>
companion object Key : Message.Key<Image> {
override val typeName: String
get() = "Image"
}
/**
* 图片的 id.
@ -92,7 +95,10 @@ sealed class AbstractImage : Image {
* 一般由 [Contact.uploadImage] 得到
*/
interface OnlineImage : Image {
companion object Key : Message.Key<OnlineImage>
companion object Key : Message.Key<OnlineImage> {
override val typeName: String
get() = "OnlineImage"
}
/**
* 原图下载链接. 包含域名
@ -124,7 +130,10 @@ suspend fun Image.queryUrl(): String {
* 一般由 [Contact.uploadImage] 得到
*/
interface OfflineImage : Image {
companion object Key : Message.Key<OfflineImage>
companion object Key : Message.Key<OfflineImage> {
override val typeName: String
get() = "OfflineImage"
}
}
/**
@ -149,7 +158,10 @@ suspend fun OfflineImage.queryUrl(): String {
// CustomFace
@OptIn(MiraiInternalAPI::class)
sealed class GroupImage : AbstractImage() {
companion object Key : Message.Key<GroupImage>
companion object Key : Message.Key<GroupImage> {
override val typeName: String
get() = "GroupImage"
}
abstract val filepath: String
abstract val fileId: Int
@ -222,7 +234,10 @@ abstract class OnlineGroupImage : GroupImage(), OnlineImage
*/ // NotOnlineImage
@OptIn(MiraiInternalAPI::class)
sealed class FriendImage : AbstractImage() {
companion object Key : Message.Key<FriendImage>
companion object Key : Message.Key<FriendImage> {
override val typeName: String
get() = "FriendImage"
}
abstract val resourceId: String
abstract val md5: ByteArray

View File

@ -26,9 +26,9 @@ import kotlin.jvm.JvmSynthetic
* - [MessageMetadata] 消息元数据, 包括: [消息来源][MessageSource]
* - [MessageContent] 单个消息, 包括: [纯文本][PlainText], [@群员][At], [@全体成员][AtAll] .
* - [CombinedMessage] 通过 [plus] 连接的两个消息. 可通过 [asMessageChain] 转换为 [MessageChain]
* - [MessageChain] 不可变消息链, [List] 形式链接的多个 [Message] 实例.
* - [MessageChain] 不可变消息链, 链表形式链接的多个 [SingleMessage] 实例.
*
* ** Kotlin 使用 [Message]**
* #### Kotlin 使用 [Message]:
* 这与使用 [String] 的使用非常类似.
*
* 比较 [Message] [String] (使用 infix [Message.eq]):
@ -72,7 +72,13 @@ interface Message {
*
* @param M 指代持有这个 Key 的消息类型
*/
interface Key<out M : Message>
interface Key<out M : Message> {
/**
* [Key] 指代的 [Message] 类型名. 一般为 `class.simpleName`
*/
@SinceMirai("0.34.0")
val typeName: String
}
infix fun eq(other: Message): Boolean = this.toString() == other.toString()
@ -105,7 +111,7 @@ interface Message {
if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>
&& this.key == tail.key
) {
return CombinedMessage(EmptyMessageChain, this)
return CombinedMessage(EmptyMessageChain, tail)
}
return CombinedMessage(left = this, tail = tail)
}
@ -119,6 +125,8 @@ interface Message {
* [FriendImage]: "[mirai:image:/f8f1ab55-bf8e-4236-b55e-955848d7069f]"
* [PokeMessage]: "[mirai:poke:1,-1]"
* [MessageChain]: 直接无间隔地连接所有元素.
*
* @see contentToString
*/
override fun toString(): String
@ -141,17 +149,23 @@ interface Message {
operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage())
}
@JvmSynthetic
@Suppress("UNCHECKED_CAST")
suspend inline fun <C : Contact> Message.sendTo(contact: C): MessageReceipt<C> {
return contact.sendMessage(this) as MessageReceipt<C>
}
fun Message.repeat(count: Int): MessageChain {
if (this is ConstrainSingle<*>) {
// fast-path
return SingleMessageChainImpl(this)
}
return buildMessageChain(count) {
add(this@repeat)
}
}
@JvmSynthetic
inline operator fun Message.times(count: Int): MessageChain = this.repeat(count)
interface SingleMessage : Message, CharSequence, Comparable<String>
@ -168,13 +182,13 @@ interface MessageMetadata : SingleMessage {
}
/**
* 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 但不会保持原顺序.
* 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 保持原顺序.
*
* **MiraiExperimentalAPI**: API 可能在将来版本修改
*/
@SinceMirai("0.34.0")
@MiraiExperimentalAPI
interface ConstrainSingle<M : Message> {
interface ConstrainSingle<M : Message> : SingleMessage {
val key: Message.Key<M>
}

View File

@ -26,7 +26,7 @@ import kotlin.reflect.KProperty
/**
* 消息链.
* 它的一般实现为 [MessageChainImplByIterable] [MessageChainImplBySequence],
* 它的一般实现为 [MessageChainImplByCollection] [MessageChainImplBySequence],
* 替代 `null` 情况的实现为 [NullMessageChain],
* 空的实现为 [EmptyMessageChain]
*
@ -170,7 +170,7 @@ fun <M : Message> MessageChain.firstOrNull(key: Message.Key<M>): M? = when (key)
@JvmSynthetic
@Suppress("UNCHECKED_CAST")
inline fun <M : Message> MessageChain.first(key: Message.Key<M>): M =
firstOrNull(key) ?: throw NoSuchElementException("no such element: $key")
firstOrNull(key) ?: throw NoSuchElementException("Message type $key not found in chain $this")
/**
* 获取第一个 [M] 类型的 [Message] 实例
@ -264,7 +264,8 @@ fun Message.asMessageChain(): MessageChain = when (this) {
* 直接将 [this] 委托为一个 [MessageChain]
*/
@JvmSynthetic
inline fun Collection<SingleMessage>.asMessageChain(): MessageChain = MessageChainImplByCollection(this)
fun Collection<SingleMessage>.asMessageChain(): MessageChain =
MessageChainImplByCollection(this.constrainSingleMessages())
/**
* [this] [扁平化后][flatten] 委托为一个 [MessageChain]
@ -277,7 +278,8 @@ inline fun Collection<Message>.asMessageChain(): MessageChain = MessageChainImpl
* 直接将 [this] 委托为一个 [MessageChain]
*/
@JvmSynthetic
inline fun Iterable<SingleMessage>.asMessageChain(): MessageChain = MessageChainImplByIterable(this)
fun Iterable<SingleMessage>.asMessageChain(): MessageChain =
MessageChainImplByCollection(this.constrainSingleMessages())
@JvmSynthetic
inline fun MessageChain.asMessageChain(): MessageChain = this // 避免套娃
@ -386,6 +388,7 @@ fun Message.flatten(): Sequence<SingleMessage> {
}
}
@JvmSynthetic // make Java user happier with less methods
fun CombinedMessage.flatten(): Sequence<SingleMessage> {
// already constrained single.
if (this.isFlat()) {
@ -394,6 +397,7 @@ fun CombinedMessage.flatten(): Sequence<SingleMessage> {
} else return this.asSequence().flatten()
}
@JvmSynthetic // make Java user happier with less methods
inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() // fast path
// endregion converters
@ -402,7 +406,15 @@ inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() /
/**
* 不含任何元素的 [MessageChain]
*/
object EmptyMessageChain : MessageChain by MessageChainImplByCollection(emptyList())
object EmptyMessageChain : MessageChain, Iterator<SingleMessage> {
override fun contains(sub: String): Boolean = sub.isEmpty()
override val size: Int get() = 0
override fun toString(): String = ""
override fun contentToString(): String = ""
override fun iterator(): Iterator<SingleMessage> = this
override fun hasNext(): Boolean = false
override fun next(): SingleMessage = throw NoSuchElementException("EmptyMessageChain is empty.")
}
/**
* Null [MessageChain].
@ -427,22 +439,45 @@ object NullMessageChain : MessageChain {
// region implementations
/**
* 使用 [Iterable] 作为委托的 [MessageChain]
*/
@PublishedApi
internal class MessageChainImplByIterable constructor(
private val delegate: Iterable<SingleMessage>
) : Message, Iterable<SingleMessage>, MessageChain {
override val size: Int by lazy { delegate.count() }
override fun iterator(): Iterator<SingleMessage> = delegate.iterator()
private var toStringTemp: String? = null
override fun toString(): String =
toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it }
@Suppress("DuplicatedCode") // we don't have pattern matching
@OptIn(MiraiExperimentalAPI::class)
internal fun Sequence<SingleMessage>.constrainSingleMessages(): List<SingleMessage> {
val list = ArrayList<SingleMessage>(4)
val singleList = ArrayList<Message.Key<*>?>(4)
override fun contentToString(): String = toString()
for (singleMessage in this) {
if (singleMessage is ConstrainSingle<*>) {
val key = singleMessage.key
val index = singleList.indexOf(key)
if (index != -1) {
list[index] = singleMessage
continue
} else {
singleList.add(list.size, key)
}
}
list.add(singleMessage)
}
return list
}
override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
@Suppress("DuplicatedCode") // we don't have pattern matching
@OptIn(MiraiExperimentalAPI::class)
internal fun Iterable<SingleMessage>.constrainSingleMessages(): List<SingleMessage> {
val list = ArrayList<SingleMessage>()
for (singleMessage in this) {
if (singleMessage is ConstrainSingle<*>) {
val key = singleMessage.key
val index = list.indexOfFirst { it is ConstrainSingle<*> && it.key == key }
if (index != -1) {
list[index] = singleMessage
continue
}
}
list.add(singleMessage)
}
return list
}
/**
@ -450,7 +485,7 @@ internal class MessageChainImplByIterable constructor(
*/
@PublishedApi
internal class MessageChainImplByCollection constructor(
private val delegate: Collection<SingleMessage>
private val delegate: Collection<SingleMessage> // 必须 constrainSingleMessages, 且为 immutable
) : Message, Iterable<SingleMessage>, MessageChain {
override val size: Int get() = delegate.size
override fun iterator(): Iterator<SingleMessage> = delegate.iterator()
@ -458,7 +493,10 @@ internal class MessageChainImplByCollection constructor(
override fun toString(): String =
toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it }
override fun contentToString(): String = toString()
private var contentToStringTemp: String? = null
override fun contentToString(): String =
contentToStringTemp ?: this.delegate.joinToString("") { it.contentToString() }.also { contentToStringTemp = it }
override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
}
@ -468,20 +506,24 @@ internal class MessageChainImplByCollection constructor(
*/
@PublishedApi
internal class MessageChainImplBySequence constructor(
delegate: Sequence<SingleMessage>
delegate: Sequence<SingleMessage> // 可以有重复 ConstrainSingle
) : Message, Iterable<SingleMessage>, MessageChain {
override val size: Int by lazy { collected.size }
/**
* [Sequence] 可能只能消耗一遍, 因此需要先转为 [List]
*/
private val collected: List<SingleMessage> by lazy { delegate.toList() }
private val collected: List<SingleMessage> by lazy { delegate.constrainSingleMessages() }
override fun iterator(): Iterator<SingleMessage> = collected.iterator()
private var toStringTemp: String? = null
override fun toString(): String =
toStringTemp ?: this.collected.joinToString("") { it.toString() }.also { toStringTemp = it }
override fun contentToString(): String = toString()
private var contentToStringTemp: String? = null
override fun contentToString(): String =
contentToStringTemp ?: this.collected.joinToString("") { it.contentToString() }
.also { contentToStringTemp = it }
override operator fun contains(sub: String): Boolean = collected.any { it.contains(sub) }
}
@ -495,7 +537,7 @@ internal class SingleMessageChainImpl constructor(
) : Message, Iterable<SingleMessage>, MessageChain {
override val size: Int get() = 1
override fun toString(): String = this.delegate.toString()
override fun contentToString(): String = this.delegate.toString()
override fun contentToString(): String = this.delegate.contentToString()
override fun iterator(): Iterator<SingleMessage> = iterator { yield(delegate) }
override operator fun contains(sub: String): Boolean = sub in delegate
}

View File

@ -42,7 +42,10 @@ import kotlin.jvm.JvmSynthetic
*/
@SinceMirai("0.33.0")
sealed class MessageSource : Message, MessageMetadata {
companion object Key : Message.Key<MessageSource>
companion object Key : Message.Key<MessageSource> {
override val typeName: String
get() = "MessageSource"
}
/**
* 所属 [Bot]
@ -105,7 +108,10 @@ sealed class MessageSource : Message, MessageMetadata {
@SinceMirai("0.33.0")
@OptIn(MiraiExperimentalAPI::class)
sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessageSource> {
companion object Key : Message.Key<OnlineMessageSource>
companion object Key : Message.Key<OnlineMessageSource> {
override val typeName: String
get() = "OnlineMessageSource"
}
override val key: Message.Key<OnlineMessageSource> get() = Key
@ -131,7 +137,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
* [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
*/
sealed class Outgoing : OnlineMessageSource() {
companion object Key : Message.Key<Outgoing>
companion object Key : Message.Key<Outgoing> {
override val typeName: String
get() = "OnlineMessageSource.Outgoing"
}
abstract override val sender: Bot
abstract override val target: Contact
@ -140,7 +149,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
final override val targetId: Long get() = target.id
abstract class ToFriend : Outgoing() {
companion object Key : Message.Key<ToFriend>
companion object Key : Message.Key<ToFriend> {
override val typeName: String
get() = "OnlineMessageSource.Outgoing.ToFriend"
}
abstract override val target: QQ
final override val subject: QQ get() = target
@ -148,7 +160,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
}
abstract class ToGroup : Outgoing() {
companion object Key : Message.Key<ToGroup>
companion object Key : Message.Key<ToGroup> {
override val typeName: String
get() = "OnlineMessageSource.Outgoing.ToGroup"
}
abstract override val target: Group
final override val subject: Group get() = target
@ -160,7 +175,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
* 接收到的一条消息的 [MessageSource]
*/
sealed class Incoming : OnlineMessageSource() {
companion object Key : Message.Key<Incoming>
companion object Key : Message.Key<Incoming> {
override val typeName: String
get() = "OnlineMessageSource.Incoming"
}
abstract override val sender: QQ // out QQ
abstract override val target: Bot
@ -169,7 +187,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
final override val targetId: Long get() = target.id
abstract class FromFriend : Incoming() {
companion object Key : Message.Key<FromFriend>
companion object Key : Message.Key<FromFriend> {
override val typeName: String
get() = "OnlineMessageSource.Incoming.FromFriend"
}
abstract override val sender: QQ
final override val subject: QQ get() = sender
@ -177,7 +198,10 @@ sealed class OnlineMessageSource : MessageSource(), ConstrainSingle<OnlineMessag
}
abstract class FromGroup : Incoming() {
companion object Key : Message.Key<FromGroup>
companion object Key : Message.Key<FromGroup> {
override val typeName: String
get() = "OnlineMessageSource.Incoming.FromGroup"
}
abstract override val sender: Member
final override val subject: Group get() = sender.group
@ -224,7 +248,10 @@ inline fun MessageSource.recallIn(
*/
@SinceMirai("0.33.0")
abstract class OfflineMessageSource : MessageSource() {
companion object Key : Message.Key<OfflineMessageSource>
companion object Key : Message.Key<OfflineMessageSource> {
override val typeName: String
get() = "OfflineMessageSource"
}
enum class Kind {
GROUP,

View File

@ -42,7 +42,10 @@ class PlainText(val stringValue: String) :
return stringValue.hashCode()
}
companion object Key : Message.Key<PlainText>
companion object Key : Message.Key<PlainText> {
override val typeName: String
get() = "PlainText"
}
}
/**

View File

@ -21,6 +21,8 @@ import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmSynthetic
/**
@ -34,8 +36,10 @@ import kotlin.jvm.JvmName
@OptIn(MiraiExperimentalAPI::class)
@SinceMirai("0.33.0")
class QuoteReply(val source: MessageSource) : Message, MessageMetadata, ConstrainSingle<QuoteReply> {
// TODO: 2020/4/4 Metadata or Content?
companion object Key : Message.Key<QuoteReply>
companion object Key : Message.Key<QuoteReply> {
override val typeName: String
get() = "QuoteReply"
}
override val key: Message.Key<QuoteReply> get() = Key
@ -43,8 +47,10 @@ class QuoteReply(val source: MessageSource) : Message, MessageMetadata, Constrai
override fun contentToString(): String = ""
}
@JvmSynthetic
suspend inline fun QuoteReply.recall() = this.source.recall()
@JvmOverloads
inline fun QuoteReply.recallIn(
millis: Long,
coroutineContext: CoroutineContext = EmptyCoroutineContext

View File

@ -36,14 +36,6 @@ interface RichMessage : MessageContent {
@SinceMirai("0.30.0")
companion object Templates : Message.Key<RichMessage> {
/**
* 合并转发.
*/
@MiraiExperimentalAPI
fun mergedForward(): Nothing {
TODO()
}
/**
* 长消息.
*
@ -100,6 +92,9 @@ interface RichMessage : MessageContent {
}
}
}
override val typeName: String
get() = "RichMessage"
}
val content: String
@ -118,7 +113,10 @@ interface RichMessage : MessageContent {
@SinceMirai("0.27.0")
@OptIn(MiraiExperimentalAPI::class)
class JsonMessage(override val content: String) : RichMessage {
companion object Key : Message.Key<JsonMessage>
companion object Key : Message.Key<JsonMessage> {
override val typeName: String
get() = "JsonMessage"
}
// serviceId = 1
override fun toString(): String = "[mirai:json:$content]"
@ -133,7 +131,10 @@ class JsonMessage(override val content: String) : RichMessage {
@SinceMirai("0.27.0")
class LightApp constructor(override val content: String) : RichMessage {
companion object Key : Message.Key<LightApp>
companion object Key : Message.Key<LightApp> {
override val typeName: String
get() = "LightApp"
}
override fun toString(): String = "[mirai:app:$content]"
}
@ -147,7 +148,10 @@ class LightApp constructor(override val content: String) : RichMessage {
@SinceMirai("0.27.0")
@OptIn(MiraiExperimentalAPI::class)
class XmlMessage constructor(override val content: String) : RichMessage {
companion object Key : Message.Key<XmlMessage>
companion object Key : Message.Key<XmlMessage> {
override val typeName: String
get() = "XmlMessage"
}
// override val serviceId: Int get() = 60
@ -161,7 +165,10 @@ class XmlMessage constructor(override val content: String) : RichMessage {
@MiraiExperimentalAPI
@MiraiInternalAPI
class LongMessage(override val content: String, val resId: String) : RichMessage {
companion object Key : Message.Key<XmlMessage>
companion object Key : Message.Key<XmlMessage> {
override val typeName: String
get() = "LongMessage"
}
// serviceId = 35
override fun toString(): String = "[mirai:long:$content]"

View File

@ -0,0 +1,89 @@
/*
* 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.message.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertSame
internal class ConstrainSingleTest {
@OptIn(MiraiExperimentalAPI::class)
internal class TestMessage : ConstrainSingle<TestMessage>, Any() {
companion object Key : Message.Key<TestMessage> {
override val typeName: String
get() = "TestMessage"
}
override fun toString(): String = super.toString()
override fun contentToString(): String {
TODO("Not yet implemented")
}
override val key: Message.Key<TestMessage>
get() = Key
override val length: Int
get() = TODO("Not yet implemented")
override fun get(index: Int): Char {
TODO("Not yet implemented")
}
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
TODO("Not yet implemented")
}
override fun compareTo(other: String): Int {
TODO("Not yet implemented")
}
}
@OptIn(MiraiExperimentalAPI::class)
@Test
fun testConstrainSingleInPlus() {
val new = TestMessage()
val combined = TestMessage() + new
assertEquals(combined.left, EmptyMessageChain)
assertSame(combined.tail, new)
}
@Test // net.mamoe.mirai/message/data/MessageChain.kt:441
fun testConstrainSingleInSequence() {
val last = TestMessage()
val sequence: Sequence<SingleMessage> = sequenceOf(
TestMessage(),
TestMessage(),
last
)
val result = sequence.constrainSingleMessages()
assertEquals(result.count(), 1)
assertSame(result.single(), last)
}
@Test // net.mamoe.mirai/message/data/MessageChain.kt:441
fun testConstrainSingleOrderInSequence() {
val last = TestMessage()
val sequence: Sequence<SingleMessage> = sequenceOf(
TestMessage(), // last should replace here
PlainText("test"),
TestMessage(),
last
)
val result = sequence.constrainSingleMessages()
assertEquals(result.count(), 2)
assertSame(result.first(), last)
}
}