mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-15 07:37:08 +08:00
Implement ConstrainSingle
This commit is contained in:
parent
4d6085c006
commit
3714b1b95e
mirai-core/src
commonMain/kotlin/net.mamoe.mirai/message/data
At.ktAtAll.ktFace.ktHummerMessage.ktImage.ktMessage.ktMessageChain.ktMessageSource.ktPlainText.ktQuoteReply.ktRichMessage.kt
commonTest/kotlin/net/mamoe/mirai/message.data
@ -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], 仅供内部使用, 否则可能造成消息无法发出的问题.
|
||||
*/
|
||||
|
@ -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]"
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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]"
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user