From ebc7d655e337b462ed71c7de44865a0bd33ff586 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 6 Jan 2021 13:13:39 +0800 Subject: [PATCH] Message serialization, fix #767 --- .../kotlin/message/MessageSerializer.kt | 161 ++++++++---------- .../src/commonMain/kotlin/message/data/At.kt | 8 +- .../commonMain/kotlin/message/data/AtAll.kt | 5 +- .../commonMain/kotlin/message/data/Face.kt | 9 +- .../kotlin/message/data/ForwardMessage.kt | 39 +++-- .../kotlin/message/data/HummerMessage.kt | 19 ++- .../commonMain/kotlin/message/data/Image.kt | 57 ++++--- .../commonMain/kotlin/message/data/Message.kt | 65 +------ .../kotlin/message/data/MessageChain.kt | 15 +- .../kotlin/message/data/MessageKey.kt | 1 - .../kotlin/message/data/MessageSource.kt | 11 +- .../kotlin/message/data/PlainText.kt | 8 +- .../kotlin/message/data/QuoteReply.kt | 12 +- .../kotlin/message/data/RichMessage.kt | 17 +- .../commonMain/kotlin/message/data/Voice.kt | 8 +- .../commonMain/kotlin/message/data/impl.kt | 2 +- mirai-core/src/commonMain/kotlin/MiraiImpl.kt | 80 ++++++--- .../commonMain/kotlin/contact/GroupImpl.kt | 4 +- .../kotlin/contact/NormalMemberImpl.kt | 6 +- .../src/commonMain/kotlin/contact/util.kt | 18 +- .../commonMain/kotlin/message/conversions.kt | 14 +- .../src/commonMain/kotlin/message/faceImpl.kt | 4 +- .../commonMain/kotlin/message/imagesImpl.kt | 20 ++- .../kotlin/message/incomingSourceImpl.kt | 23 ++- .../kotlin/message/offlineSourceImpl.kt | 150 +++++++--------- .../kotlin/message/outgoingSourceImpl.kt | 26 ++- .../network/protocol/packet/chat/MultiMsg.kt | 4 +- .../chat/receive/MessageSvc.PbGetMsg.kt | 10 +- .../chat/receive/MessageSvc.PbSendMsg.kt | 26 +-- .../message/data/MessageSerializationTest.kt | 24 ++- 30 files changed, 452 insertions(+), 394 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/MessageSerializer.kt b/mirai-core-api/src/commonMain/kotlin/message/MessageSerializer.kt index 276cc35ce..5931c4cfd 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/MessageSerializer.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/MessageSerializer.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. @@ -10,15 +10,12 @@ package net.mamoe.mirai.message import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.plus -import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.* import net.mamoe.mirai.Mirai import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiExperimentalApi @@ -29,21 +26,44 @@ import kotlin.reflect.KClass public interface MessageSerializer { public val serializersModule: SerializersModule - public fun registerSerializer(clazz: KClass, serializer: KSerializer) + public fun registerSerializer(baseClass: KClass, serializer: KSerializer) public fun registerSerializers(serializersModule: SerializersModule) public fun clearRegisteredSerializers() + + public companion object INSTANCE : MessageSerializer by MessageSerializerImpl +} + +internal fun ClassSerialDescriptorBuilder.takeElementsFrom(descriptor: SerialDescriptor) { + with(descriptor) { + repeat(descriptor.elementsCount) { index -> + element( + elementName = getElementName(index), + descriptor = getElementDescriptor(index), + annotations = getElementAnnotations(index), + isOptional = isElementOptional(index), + ) + } + } } @MiraiInternalApi -public open class MessageSourceSerializerImpl(serialName: String) : KSerializer { - public companion object : MessageSourceSerializerImpl("net.mamoe.mirai.message.data.MessageSource") - +public open class MessageSourceSerializerImpl(serialName: String) : + KSerializer by SerialData.serializer().map( + resultantDescriptor = buildClassSerialDescriptor(serialName) { + takeElementsFrom(SerialData.serializer().descriptor) + }, + serialize = { SerialData(kind, botId, ids, internalIds, time, fromId, targetId, originalMessage) }, + deserialize = { + Mirai.constructMessageSource(botId, kind, fromId, targetId, ids, time, internalIds, originalMessage) + } + ) { + @SerialName(MessageSource.SERIAL_NAME) @Serializable internal class SerialData( val kind: MessageSourceKind, - val bot: Long, + val botId: Long, val ids: IntArray, val internalIds: IntArray, val time: Int, @@ -51,49 +71,6 @@ public open class MessageSourceSerializerImpl(serialName: String) : KSerializer< val targetId: Long, val originalMessage: MessageChain, ) - - override val descriptor: SerialDescriptor = buildClassSerialDescriptor(serialName) { - val desc = SerialData.serializer().descriptor - repeat(SerialData.serializer().descriptor.elementsCount) { index -> - element( - desc.getElementName(index), - desc.getElementDescriptor(index), - desc.getElementAnnotations(index), - desc.isElementOptional(index) - ) - } - } -// buildClassSerialDescriptor("MessageSource") { -// element("bot", Long.serializer().descriptor) -// element("ids", ArraySerializer(Int.serializer()).descriptor) -// element("internalIds", ArraySerializer(Int.serializer()).descriptor) -// element("time", Int.serializer().descriptor) -// element("fromId", Int.serializer().descriptor) -// element("targetId", Int.serializer().descriptor) -// element("originalMessage", MessageChain.Serializer.descriptor) -// } - - override fun deserialize(decoder: Decoder): MessageSource { - val data = SerialData.serializer().deserialize(decoder) - data.run { - return Mirai.constructMessageSource( - botId = bot, kind = kind, fromUin = fromId, targetUin = targetId, ids = ids, - time = time, internalIds = internalIds, originalMessage = originalMessage.asMessageChain() - ) - } - } - - override fun serialize(encoder: Encoder, value: MessageSource) { - value.run { - SerialData.serializer().serialize( - encoder = encoder, - value = SerialData( - kind = kind, bot = botId, ids = ids, internalIds = internalIds, - time = time, fromId = fromId, targetId = targetId, originalMessage = originalMessage - ) - ) - } - } } @@ -121,8 +98,6 @@ private val builtInSerializersModule by lazy { contextual(LightApp::class, LightApp.serializer()) contextual(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) contextual(AbstractServiceMessage::class, AbstractServiceMessage.serializer()) - contextual(LongMessage::class, LongMessage.serializer()) - contextual(ForwardMessageInternal::class, ForwardMessageInternal.serializer()) contextual(PttMessage::class, PttMessage.serializer()) contextual(Voice::class, Voice.serializer()) @@ -130,12 +105,7 @@ private val builtInSerializersModule by lazy { contextual(VipFace::class, VipFace.serializer()) contextual(FlashImage::class, FlashImage.serializer()) - fun PolymorphicModuleBuilder.singleMessageSubclasses() { - // subclass(MessageSource::class, MessageSource.serializer()) - } - - // contextual(MessageSource::class, MessageSource.serializer()) - polymorphicDefault(MessageSource::class) { MessageSource.serializer() } + contextual(MessageSource::class, MessageSource.serializer()) fun PolymorphicModuleBuilder.messageMetadataSubclasses() { subclass(MessageSource::class, MessageSource.serializer()) @@ -154,8 +124,6 @@ private val builtInSerializersModule by lazy { subclass(LightApp::class, LightApp.serializer()) subclass(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) - subclass(LongMessage::class, LongMessage.serializer()) - subclass(ForwardMessageInternal::class, ForwardMessageInternal.serializer()) // subclass(PttMessage::class, PttMessage.serializer()) subclass(Voice::class, Voice.serializer()) @@ -166,21 +134,11 @@ private val builtInSerializersModule by lazy { subclass(FlashImage::class, FlashImage.serializer()) } - @Suppress("DEPRECATION_ERROR") - contextual(Message::class, Message.Serializer) - // contextual(SingleMessage::class, SingleMessage.Serializer) + contextual(SingleMessage::class, SingleMessage.Serializer) contextual(MessageChain::class, MessageChain.Serializer) contextual(MessageChainImpl::class, MessageChainImpl.serializer()) - polymorphic(MessageChain::class) { - subclass(MessageChainImpl::class, MessageChainImpl.serializer()) - } - polymorphicDefault(MessageChain::class) { MessageChainImpl.serializer() } - - polymorphic(AbstractServiceMessage::class) { - subclass(LongMessage::class, LongMessage.serializer()) - subclass(ForwardMessageInternal::class, ForwardMessageInternal.serializer()) - } +// polymorphicDefault(MessageChain::class) { MessageChainImpl.serializer() } // polymorphic(SingleMessage::class) { // subclass(MessageSource::class, MessageSource.serializer()) @@ -189,16 +147,12 @@ private val builtInSerializersModule by lazy { // } // } - polymorphicDefault(Image::class) { Image.Serializer } - // polymorphic(Message::class) { // subclass(PlainText::class, PlainText.serializer()) // } - polymorphic(Message::class) { + polymorphic(SingleMessage::class) { messageContentSubclasses() messageMetadataSubclasses() - singleMessageSubclasses() - subclass(MessageChainImpl::class, MessageChainImpl.serializer()) } //contextual(SingleMessage::class, SingleMessage.Serializer) @@ -216,7 +170,7 @@ private val builtInSerializersModule by lazy { // contextual(MessageMetadata::class, MessageMetadata.Serializer) // polymorphic(MessageMetadata::class, MessageMetadata.Serializer) { // messageMetadataSubclasses() - // } + // } } @@ -226,22 +180,51 @@ internal object MessageSerializerImpl : MessageSerializer { override val serializersModule: SerializersModule get() = serializersModuleField ?: builtInSerializersModule @Synchronized - override fun registerSerializer(clazz: KClass, serializer: KSerializer) { - serializersModuleField = serializersModule.plus(SerializersModule { - contextual(clazz, serializer) - polymorphic(Message::class) { - subclass(clazz, serializer) + override fun registerSerializer(baseClass: KClass, serializer: KSerializer) { + serializersModuleField = serializersModule.overwriteWith(SerializersModule { + contextual(baseClass, serializer) + polymorphic(SingleMessage::class) { + subclass(baseClass, serializer) } }) } @Synchronized override fun registerSerializers(serializersModule: SerializersModule) { - serializersModuleField = serializersModule + serializersModuleField = serializersModule.overwriteWith(serializersModule) } @Synchronized override fun clearRegisteredSerializers() { serializersModuleField = builtInSerializersModule } +} + +internal inline fun KSerializer.map( + resultantDescriptor: SerialDescriptor, + crossinline deserialize: T.(T) -> R, + crossinline serialize: R.(R) -> T, +): KSerializer { + return object : KSerializer { + override val descriptor: SerialDescriptor get() = resultantDescriptor + override fun deserialize(decoder: Decoder): R = this@map.deserialize(decoder).let { deserialize(it, it) } + override fun serialize(encoder: Encoder, value: R) = serialize(encoder, value.let { serialize(it, it) }) + } +} + +internal inline fun KSerializer.mapPrimitive( + serialName: String, + crossinline deserialize: (T) -> R, + crossinline serialize: R.(R) -> T, +): KSerializer { + val kind = this@mapPrimitive.descriptor.kind + check(kind is PrimitiveKind) { "kind must be PrimitiveKind but found $kind" } + return object : KSerializer { + override fun deserialize(decoder: Decoder): R = + this@mapPrimitive.deserialize(decoder).let(deserialize) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(serialName, kind) + override fun serialize(encoder: Encoder, value: R) = + this@mapPrimitive.serialize(encoder, value.let { serialize(it, it) }) + } } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/At.kt b/mirai-core-api/src/commonMain/kotlin/message/data/At.kt index 40e3e9370..203fe08d0 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/At.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/At.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. @@ -14,6 +14,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member @@ -30,6 +31,7 @@ import net.mamoe.mirai.message.code.CodableMessage * * @see AtAll 全体成员 */ +@SerialName(At.SERIAL_NAME) @Serializable public data class At( public val target: Long, @@ -51,7 +53,9 @@ public data class At( builder.append("[mirai:at:").append(target).append(']') } - public companion object; + public companion object { + public const val SERIAL_NAME: String = "At" + } // 自动为消息补充 " " public override fun followedBy(tail: Message): MessageChain { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt b/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt index 98a205e5f..a54cadc1c 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.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. @@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.message.code.CodableMessage @@ -28,10 +29,12 @@ private const val displayA = "@全体成员" * * @see At at 单个群成员 */ +@SerialName(AtAll.SERIAL_NAME) @Serializable public object AtAll : MessageContent, CodableMessage { public const val display: String = displayA + public const val SERIAL_NAME: String = "AtAll" @Suppress("SpellCheckingInspection") public override fun toString(): String = "[mirai:atall]" diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt index b81ab0ed6..5361b21aa 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Face.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. @@ -12,6 +12,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.message.code.CodableMessage @@ -22,10 +23,11 @@ import net.mamoe.mirai.message.code.CodableMessage * 格式: [mirai:face:*[id]*] */ @Serializable +@SerialName(Face.SERIAL_NAME) public data class Face(public val id: Int) : // used in delegation MessageContent, CodableMessage { - public override fun toString(): String = "[mirai:face:$id]"; + public override fun toString(): String = "[mirai:face:$id]" public val name: String get() = contentToString().let { it.substring(1, it.length - 1) } public override fun contentToString(): String = names.getOrElse(id) { "[表情]" } @@ -39,6 +41,9 @@ public data class Face(public val id: Int) : // used in delegation //Auto generated @Suppress("NonAsciiCharacters", "unused", "SpellCheckingInspection", "all") public companion object { + public const val SERIAL_NAME: String = "Face" + + public const val JING_YA: Int = 0 public const val 惊讶: Int = JING_YA public const val PIE_ZUI: Int = 1 diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt index eb9d32c68..106b36005 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.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. @@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data import io.ktor.http.* import io.ktor.util.* +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact @@ -96,6 +97,7 @@ public data class RawForwardMessage( * * @see buildForwardMessage */ +@SerialName(ForwardMessage.SERIAL_NAME) @Serializable public data class ForwardMessage( val preview: List, @@ -136,7 +138,7 @@ public data class ForwardMessage( * Java 用户: 使用 [sequenceOf] (`SequenceKt.sequenceOf`) 或 [asSequence] (`SequenceKt.asSequence`) */ public fun generatePreview(forward: RawForwardMessage): List = - forward.nodeList.map { it.senderName + ": " + it.message.contentToString() } + forward.nodeList.map { it.senderName + ": " + it.messageChain.contentToString() } /** * 显示在卡片底部 @@ -152,8 +154,15 @@ public data class ForwardMessage( override val senderId: Long, override val time: Int, override val senderName: String, - override val message: Message - ) : INode + override val messageChain: MessageChain + ) : INode { + public constructor( + senderId: Long, + time: Int, + senderName: String, + message: Message + ) : this(senderId, time, senderName, message.asMessageChain()) + } @MiraiExperimentalApi public interface INode { @@ -175,11 +184,13 @@ public data class ForwardMessage( /** * 消息内容 */ - public val message: Message + public val messageChain: MessageChain } public companion object Key : - AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) + AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) { + public const val SERIAL_NAME: String = "ForwardMessage" + } } @@ -358,7 +369,7 @@ public class ForwardMessageBuilder private constructor( /** * 消息内容 */ - public override lateinit var message: Message + public override lateinit var messageChain: MessageChain /** @@ -410,19 +421,21 @@ public class ForwardMessageBuilder private constructor( * 指定消息内容 */ @ForwardMessageDsl - public infix fun message(message: Message): BuilderNode = this.apply { this.message = message } + public infix fun message(message: Message): BuilderNode = + this.apply { this.messageChain = message.asMessageChain() } /** * 指定消息内容 */ @ForwardMessageDsl - public infix fun message(message: String): BuilderNode = this.apply { this.message = PlainText(message) } + public infix fun message(message: String): BuilderNode = + this.apply { this.messageChain = PlainText(message).asMessageChain() } /** 添加一条消息 */ @ForwardMessageDsl public infix fun says(message: Message): ForwardMessageBuilder = this@ForwardMessageBuilder.apply { checkBuilt() - this@BuilderNode.message = message + this@BuilderNode.messageChain = message.asMessageChain() add(this@BuilderNode) } @@ -454,7 +467,7 @@ public class ForwardMessageBuilder private constructor( checkBuilt() add(BuilderNode().apply { senderId = this@says - this.message = message + this.messageChain = message.asMessageChain() }) } @@ -551,7 +564,7 @@ public class ForwardMessageBuilder private constructor( it.senderId, it.time, it.senderName, - it.message + it.messageChain ) }).render(this.displayStrategy) @@ -565,5 +578,5 @@ public class ForwardMessageBuilder private constructor( internal inline fun Int.toLongUnsigned(): Long = this.toLong().and(0xFFFF_FFFF) private fun ForwardMessage.INode.toNode(): ForwardMessage.Node { - return ForwardMessage.Node(senderId, time, senderName, message) + return ForwardMessage.Node(senderId, time, senderName, messageChain) } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt index 425297a63..7e7770e7d 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.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. @@ -13,7 +13,7 @@ package net.mamoe.mirai.message.data -import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.code.internal.appendAsMiraiCode @@ -46,6 +46,7 @@ public interface HummerMessage : MessageContent, ConstrainSingle { * * @see PokeMessage.Companion 使用伴生对象中的常量 */ +@SerialName(PokeMessage.SERIAL_NAME) @Serializable public data class PokeMessage @MiraiInternalApi constructor( /** @@ -63,6 +64,8 @@ public data class PokeMessage @MiraiInternalApi constructor( public companion object Key : AbstractPolymorphicMessageKey(HummerMessage, { it.castOrNull() }) { + public const val SERIAL_NAME: String = "PokeMessage" + /** 戳一戳 */ @JvmField public val ChuoYiChuo: PokeMessage = PokeMessage("戳一戳", 1, -1) @@ -195,7 +198,9 @@ public interface MarketFace : HummerMessage { get() = Key public companion object Key : - AbstractPolymorphicMessageKey(HummerMessage, { it.safeCast() }) + AbstractPolymorphicMessageKey(HummerMessage, { it.safeCast() }) { + public const val SERIAL_NAME: String = "MarketFace" + } override fun contentToString(): String = name } @@ -215,6 +220,7 @@ public interface MarketFace : HummerMessage { * * @see VipFace.Key 使用伴生对象中的常量 */ +@SerialName(VipFace.SERIAL_NAME) @Serializable public data class VipFace @MiraiInternalApi constructor( /** @@ -239,6 +245,8 @@ public data class VipFace @MiraiInternalApi constructor( public companion object Key : AbstractPolymorphicMessageKey(HummerMessage, { it.safeCast() }) { + public const val SERIAL_NAME: String = "VipFace" + @JvmField public val LiuLian: Kind = 9 to "榴莲" @@ -310,12 +318,14 @@ public data class VipFace @MiraiInternalApi constructor( * * @see Image 查看图片相关信息 */ +@SerialName(FlashImage.SERIAL_NAME) @Serializable public data class FlashImage( /** * 闪照的内容图片, 即一个普通图片. */ - @Contextual + @SerialName("imageId") + @Serializable(Image.AsStringSerializer::class) public val image: Image ) : MessageContent, HummerMessage, CodableMessage, ConstrainSingle { override val key: MessageKey @@ -323,6 +333,7 @@ public data class FlashImage( public companion object Key : AbstractPolymorphicMessageKey(HummerMessage, { it.safeCast() }) { + public const val SERIAL_NAME: String = "FlashImage" /** * 将普通图片转换为闪照. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt index 96094aa28..259c098ed 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt @@ -20,14 +20,10 @@ package net.mamoe.mirai.message.data import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.encoding.decodeStructure -import kotlinx.serialization.encoding.encodeStructure import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.IMirai @@ -40,6 +36,8 @@ import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1 import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2 import net.mamoe.mirai.message.data.Image.Key.queryUrl +import net.mamoe.mirai.message.map +import net.mamoe.mirai.message.mapPrimitive import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage @@ -90,26 +88,32 @@ public interface Image : Message, MessageContent, CodableMessage { */ public val imageId: String - public object Serializer : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor("net.mamoe.mirai.message.data.Image") { - element("imageId", String.serializer().descriptor) - } + @kotlinx.serialization.Serializer(forClass = Image::class) + public object AsStringSerializer : KSerializer by String.serializer().mapPrimitive( + SERIAL_NAME, + serialize = { imageId }, + deserialize = { Image(it) }, + ) - override fun deserialize(decoder: Decoder): Image = decoder.decodeStructure(descriptor) { - val imageId = if (decodeSequentially()) { - decodeStringElement(descriptor, 0) - } else { - decodeStringElement(descriptor, decodeElementIndex(descriptor)) - } - Image(imageId) - } + @kotlinx.serialization.Serializer(forClass = Image::class) + public object Serializer : KSerializer by FallbackSerializer("Image") - override fun serialize(encoder: Encoder, value: Image): Unit = encoder.encodeStructure(descriptor) { - encodeStringElement(descriptor, 0, value.imageId) - } + @MiraiInternalApi + public open class FallbackSerializer(serialName: String) : KSerializer by Delegate.serializer().map( + buildClassSerialDescriptor(serialName) { element("imageId", String.serializer().descriptor) }, + serialize = { Delegate(imageId) }, + deserialize = { Image(imageId) }, + ) { + @SerialName(SERIAL_NAME) + @Serializable + internal data class Delegate( + val imageId: String + ) } public companion object Key : AbstractMessageKey({ it.safeCast() }) { + public const val SERIAL_NAME: String = "Image" + /** * 通过 [Image.imageId] 构造一个 [Image] 以便发送. * 这个图片必须是服务器已经存在的图片. @@ -148,8 +152,9 @@ public interface Image : Message, MessageContent, CodableMessage { @Suppress("RegExpRedundantEscape") // This is required on Android @JvmStatic @get:JvmName("getImageIdRegex") - public val IMAGE_ID_REGEX: Regex = - Regex("""\{[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\}\..{3,5}""") + // inline because compilation error + public inline val IMAGE_ID_REGEX: Regex + get() = Regex("""\{[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\}\..{3,5}""") /** * 图片资源 ID 正则表达式 1. mirai 内部使用. @@ -160,7 +165,9 @@ public interface Image : Message, MessageContent, CodableMessage { @JvmStatic @MiraiInternalApi @get:JvmName("getImageResourceIdRegex1") - public val IMAGE_RESOURCE_ID_REGEX_1: Regex = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""") + // inline because compilation error + public inline val IMAGE_RESOURCE_ID_REGEX_1: Regex + get() = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""") /** * 图片资源 ID 正则表达式 2. mirai 内部使用. @@ -171,7 +178,9 @@ public interface Image : Message, MessageContent, CodableMessage { @JvmStatic @MiraiInternalApi @get:JvmName("getImageResourceIdRegex2") - public val IMAGE_RESOURCE_ID_REGEX_2: Regex = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""") + // inline because compilation error + public inline val IMAGE_RESOURCE_ID_REGEX_2: Regex + get() = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""") } } diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt index 192e9e551..d3aed4bad 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Message.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. @@ -20,12 +20,9 @@ package net.mamoe.mirai.message.data import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.fold import kotlinx.serialization.* -import kotlinx.serialization.json.Json import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.event.events.MessageEvent import net.mamoe.mirai.message.MessageReceipt -import net.mamoe.mirai.message.MessageSerializer -import net.mamoe.mirai.message.MessageSerializerImpl import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.safeCast import kotlin.contracts.contract @@ -73,8 +70,6 @@ import kotlin.internal.LowPriorityInOverloadResolution * * @see Contact.sendMessage 发送消息 */ -@Suppress("DEPRECATION_ERROR") -@Serializable(Message.Serializer::class) public interface Message { // must be interface. Don't consider any changes. /** @@ -198,50 +193,7 @@ public interface Message { // must be interface. Don't consider any changes. public operator fun plus(another: Sequence): MessageChain = another.fold(this, Message::plus).asMessageChain() - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) - public object Serializer : - MessageSerializer by MessageSerializerImpl, - KSerializer by PolymorphicSerializer(Message::class) - - @Suppress("DEPRECATION_ERROR") - public companion object { - /** - * 从 JSON 字符串解析 [Message] - * @see serializeToJsonString - */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) - @JvmOverloads - @JvmStatic - public fun deserializeFromJsonString( - string: String, - json: Json = Json { serializersModule = Serializer.serializersModule } - ): Message { - return json.decodeFromString(Serializer, string) - } - - /** - * 将 [Message] 序列化为 JSON 字符串. - * @see deserializeFromJsonString - */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) - @JvmOverloads - @JvmStatic - public fun Message.serializeToJsonString( - json: Json = Json { serializersModule = Serializer.serializersModule } - ): String = json.encodeToString(Serializer, this) - - /** - * 将 [Message] 序列化为指定格式的字符串. - * - * @see serializeToJsonString - * @see StringFormat.encodeToString - */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) - @ExperimentalSerializationApi - @JvmStatic - public fun Message.serializeToString(format: StringFormat): String = - format.encodeToString(Serializer, this) - } + public companion object } @@ -320,10 +272,11 @@ public inline operator fun Message.times(count: Int): MessageChain = this.repeat /** * 单个消息元素. 与之相对的是 [MessageChain], 是多个 [SingleMessage] 的集合. */ -// @Serializable(SingleMessage.Serializer::class) +@Serializable(SingleMessage.Serializer::class) public interface SingleMessage : Message { - // @kotlinx.serialization.Serializer(forClass = SingleMessage::class) - // public object Serializer : KSerializer by PolymorphicSerializer(SingleMessage::class) + @kotlinx.serialization.Serializer(forClass = SingleMessage::class) + public object Serializer : + KSerializer by PolymorphicSerializer(SingleMessage::class) } /** @@ -339,14 +292,11 @@ public interface SingleMessage : Message { * * @see ConstrainSingle 约束一个 [MessageChain] 中只存在这一种类型的元素 */ -@Serializable(MessageMetadata.Serializer::class) public interface MessageMetadata : SingleMessage { /** * 返回空字符串 */ override fun contentToString(): String = "" - - public object Serializer : KSerializer by PolymorphicSerializer(MessageMetadata::class) } /** @@ -381,11 +331,8 @@ public interface ConstrainSingle : SingleMessage { * @see ForwardMessage 合并转发 * @see Voice 语音 */ -@Serializable(MessageContent.Serializer::class) public interface MessageContent : SingleMessage { public companion object Key : AbstractMessageKey({ it.safeCast() }) - - public object Serializer : KSerializer by PolymorphicSerializer(MessageContent::class) } /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt index c0e8708e4..43790cb8b 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.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. @@ -20,6 +20,7 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import net.mamoe.mirai.event.events.MessageEvent +import net.mamoe.mirai.message.MessageSerializerImpl import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.MessageSource.Key.quote import net.mamoe.mirai.message.data.MessageSource.Key.recall @@ -92,9 +93,10 @@ public interface MessageChain : Message, List, RandomAccess, Coda public operator fun contains(key: MessageKey): Boolean = asSequence().any { key.safeCast.invoke(it) != null } + @kotlinx.serialization.Serializer(MessageChain::class) public object Serializer : KSerializer { @Suppress("DEPRECATION_ERROR") - private val delegate = ListSerializer(Message.Serializer) + private val delegate = ListSerializer(PolymorphicSerializer(SingleMessage::class)) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MessageChain = delegate.deserialize(decoder).asMessageChain() override fun serialize(encoder: Encoder, value: MessageChain): Unit = delegate.serialize(encoder, value) @@ -110,12 +112,11 @@ public interface MessageChain : Message, List, RandomAccess, Coda * 从 JSON 字符串解析 [MessageChain] * @see serializeToJsonString */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) @JvmOverloads @JvmStatic public fun deserializeFromJsonString( string: String, - json: Json = Json { serializersModule = Message.Serializer.serializersModule } + json: Json = Json { serializersModule = MessageSerializerImpl.serializersModule } ): MessageChain { return json.decodeFromString(Serializer, string) } @@ -124,12 +125,11 @@ public interface MessageChain : Message, List, RandomAccess, Coda * 将 [MessageChain] 序列化为 JSON 字符串. * @see deserializeFromJsonString */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) @JvmOverloads @JvmStatic public fun MessageChain.serializeToJsonString( - json: Json = Json { serializersModule = Message.Serializer.serializersModule } - ): String = json.encodeToString(Message.Serializer, this) + json: Json = Json { serializersModule = MessageSerializerImpl.serializersModule } + ): String = json.encodeToString(MessageChain.Serializer, this) /** * 将 [MessageChain] 序列化为指定格式的字符串. @@ -137,7 +137,6 @@ public interface MessageChain : Message, List, RandomAccess, Coda * @see serializeToJsonString * @see StringFormat.encodeToString */ - @Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN) @ExperimentalSerializationApi @JvmStatic public fun MessageChain.serializeToString(format: StringFormat): String = diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt index 3b3990bf0..14d2cc370 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt @@ -36,7 +36,6 @@ public interface MessageKey { * @see AbstractPolymorphicMessageKey */ public abstract class AbstractMessageKey( - @JvmField override val safeCast: (SingleMessage) -> M?, ) : MessageKey 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 40d6a6694..299e74d54 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.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. @@ -68,7 +68,7 @@ import net.mamoe.mirai.utils.safeCast * * @see buildMessageSource 构造一个 [OfflineMessageSource] */ -@Serializable(MessageSourceSerializerImpl.Companion::class) +@Serializable(MessageSource.Serializer::class) public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle { public final override val key: MessageKey get() = Key @@ -150,7 +150,11 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle { public final override fun toString(): String = "[mirai:source:${ids.contentToString()},${internalIds.contentToString()}]" + public object Serializer : MessageSourceSerializerImpl("MessageSource") + public companion object Key : AbstractMessageKey({ it.safeCast() }) { + public const val SERIAL_NAME: String = "MessageSource" + /** * 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息. * @@ -361,7 +365,8 @@ public sealed class OnlineMessageSource : MessageSource() { } public abstract class ToStranger : Outgoing() { - public companion object Key : AbstractPolymorphicMessageKey(Outgoing, { it.safeCast() }) + public companion object Key : + AbstractPolymorphicMessageKey(Outgoing, { it.safeCast() }) public abstract override val target: Stranger public final override val subject: Stranger get() = target diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt b/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt index 9ad4dc962..3e07ea220 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.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. @@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.code.internal.appendAsMiraiCode @@ -23,6 +24,7 @@ import net.mamoe.mirai.message.code.internal.appendAsMiraiCode * 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [Message.plus] */ @Serializable +@SerialName(PlainText.SERIAL_NAME) public data class PlainText( public val content: String ) : MessageContent, CodableMessage { @@ -36,7 +38,9 @@ public data class PlainText( builder.appendAsMiraiCode(content) } - public companion object + public companion object { + public const val SERIAL_NAME: String = "PlainText" + } } /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt b/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt index 0effcbcac..4c4cde87a 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.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. @@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.Bot import net.mamoe.mirai.message.data.MessageSource.Key.recall @@ -40,8 +41,13 @@ import net.mamoe.mirai.utils.safeCast * @see MessageSource 获取有关消息源的更多信息 */ @Serializable -public data class QuoteReply(public val source: MessageSource) : Message, MessageMetadata, ConstrainSingle { - public companion object Key : AbstractMessageKey({ it.safeCast() }) +@SerialName(QuoteReply.SERIAL_NAME) +public data class QuoteReply( + public val source: MessageSource +) : Message, MessageMetadata, ConstrainSingle { + public companion object Key : AbstractMessageKey({ it.safeCast() }) { + public const val SERIAL_NAME: String = "QuoteReply" + } public override val key: MessageKey get() = Key diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt index 7244da8fc..e72a04872 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.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. @@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.code.internal.appendAsMiraiCode @@ -95,8 +96,11 @@ public interface RichMessage : MessageContent, ConstrainSingle { * @see ServiceMessage 服务消息 */ @Serializable +@SerialName(LightApp.SERIAL_NAME) public data class LightApp(override val content: String) : RichMessage, CodableMessage { - public companion object Key : AbstractMessageKey({ it.safeCast() }) + public companion object Key : AbstractMessageKey({ it.safeCast() }) { + public const val SERIAL_NAME: String = "LightApp" + } public override fun toString(): String = "[mirai:app:$content]" @@ -117,6 +121,7 @@ public data class LightApp(override val content: String) : RichMessage, CodableM */ @MiraiExperimentalApi @Serializable +@SerialName(SimpleServiceMessage.SERIAL_NAME) public class SimpleServiceMessage( public override val serviceId: Int, public override val content: String @@ -136,6 +141,9 @@ public class SimpleServiceMessage( return result } + public companion object { + public const val SERIAL_NAME: String = "SimpleServiceMessage" + } } @@ -257,8 +265,7 @@ public class XmlMessageBuilder( } } -@Serializable -@MiraiExperimentalApi +// internal runtime value, not serializable internal data class LongMessage internal constructor(override val content: String, val resId: String) : AbstractServiceMessage() { override val serviceId: Int get() = 35 @@ -266,7 +273,7 @@ internal data class LongMessage internal constructor(override val content: Strin companion object Key : AbstractPolymorphicMessageKey(ServiceMessage, { it.safeCast() }) } -@Serializable +// internal runtime value, not serializable internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage() { override val serviceId: Int get() = 35 } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt index 536a35735..b077c1616 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Voice.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. @@ -9,6 +9,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi @@ -40,6 +41,7 @@ public abstract class PttMessage : MessageContent { * 语音消息, 目前只支持接收和转发 */ @Serializable // experimental +@SerialName(Voice.SERIAL_NAME) public class Voice @MiraiInternalApi constructor( @MiraiExperimentalApi public override val fileName: String, @MiraiExperimentalApi public override val md5: ByteArray, @@ -49,7 +51,9 @@ public class Voice @MiraiInternalApi constructor( private val _url: String ) : PttMessage() { - public companion object Key : AbstractPolymorphicMessageKey(PttMessage, { it.safeCast() }) + public companion object Key : AbstractPolymorphicMessageKey(PttMessage, { it.safeCast() }) { + public const val SERIAL_NAME: String = "Voice" + } public val url: String? get() = when { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt index 568abd41b..c2ffd340f 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt @@ -156,7 +156,7 @@ internal fun MessageChain.getImpl(key: MessageKey): M? { /** * 使用 [Collection] 作为委托的 [MessageChain] */ -@Serializable +@Serializable(MessageChain.Serializer::class) internal data class MessageChainImpl constructor( @JvmField internal val delegate: List // 必须 constrainSingleMessages, 且为 immutable diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt index cf302f8af..9713106d9 100644 --- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt @@ -34,6 +34,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.MessageSerializer import net.mamoe.mirai.message.action.Nudge import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX @@ -50,17 +51,54 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { companion object INSTANCE : MiraiImpl() { @Suppress("ObjectPropertyName", "unused", "DEPRECATION_ERROR") private val _init = Mirai.let { - Message.Serializer.registerSerializer(OfflineGroupImage::class, OfflineGroupImage.serializer()) - Message.Serializer.registerSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer()) - Message.Serializer.registerSerializer(MarketFaceImpl::class, MarketFaceImpl.serializer()) - Message.Serializer.registerSerializer( + MessageSerializer.registerSerializer(OfflineGroupImage::class, OfflineGroupImage.serializer()) + MessageSerializer.registerSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer()) + MessageSerializer.registerSerializer(OnlineFriendImageImpl::class, OnlineFriendImageImpl.serializer()) + MessageSerializer.registerSerializer(OnlineGroupImageImpl::class, OnlineGroupImageImpl.serializer()) + + MessageSerializer.registerSerializer(MarketFaceImpl::class, MarketFaceImpl.serializer()) + + // MessageSource + + MessageSerializer.registerSerializer( + OnlineMessageSourceFromGroupImpl::class, + OnlineMessageSourceFromGroupImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceFromFriendImpl::class, + OnlineMessageSourceFromFriendImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceFromTempImpl::class, + OnlineMessageSourceFromTempImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceFromStrangerImpl::class, + OnlineMessageSourceFromStrangerImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceToGroupImpl::class, + OnlineMessageSourceToGroupImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceToFriendImpl::class, + OnlineMessageSourceToFriendImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceToTempImpl::class, + OnlineMessageSourceToTempImpl.serializer() + ) + MessageSerializer.registerSerializer( + OnlineMessageSourceToStrangerImpl::class, + OnlineMessageSourceToStrangerImpl.serializer() + ) + MessageSerializer.registerSerializer( OfflineMessageSourceImplData::class, OfflineMessageSourceImplData.serializer() ) - - Message.Serializer.registerSerializer( - MessageSourceFromGroupImpl::class, - MessageSourceFromGroupImpl.serializer() + MessageSerializer.registerSerializer( + OfflineMessageSourceImplData::class, + OfflineMessageSourceImplData.serializer() ) } } @@ -305,12 +343,12 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { } val response: PbMessageSvc.PbMsgWithDraw.Response = when (source) { - is MessageSourceToGroupImpl, - is MessageSourceFromGroupImpl + is OnlineMessageSourceToGroupImpl, + is OnlineMessageSourceFromGroupImpl -> { val group = when (source) { - is MessageSourceToGroupImpl -> source.target - is MessageSourceFromGroupImpl -> source.group + is OnlineMessageSourceToGroupImpl -> source.target + is OnlineMessageSourceFromGroupImpl -> source.group else -> error("stub") } if (bot.id != source.fromId) { @@ -326,10 +364,10 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { ).sendAndExpect() } } - is MessageSourceFromFriendImpl, - is MessageSourceToFriendImpl, - is MessageSourceFromStrangerImpl, - is MessageSourceToStrangerImpl, + is OnlineMessageSourceFromFriendImpl, + is OnlineMessageSourceToFriendImpl, + is OnlineMessageSourceFromStrangerImpl, + is OnlineMessageSourceToStrangerImpl, -> network.run { check(source.fromId == bot.id) { "can only recall a message sent by bot" @@ -342,13 +380,13 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { source.time ).sendAndExpect() } - is MessageSourceFromTempImpl, - is MessageSourceToTempImpl + is OnlineMessageSourceFromTempImpl, + is OnlineMessageSourceToTempImpl -> network.run { check(source.fromId == bot.id) { "can only recall a message sent by bot" } - source as MessageSourceToTempImpl + source as OnlineMessageSourceToTempImpl PbMessageSvc.PbMsgWithDraw.createForTempMessage( bot.client, (source.target.group as GroupImpl).uin, @@ -592,7 +630,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { forwardMessage: ForwardMessage? ): MessageReceipt = with(bot.asQQAndroidBot()) { message.forEach { - it.message.ensureSequenceIdAvailable() + it.messageChain.ensureSequenceIdAvailable() } val group = getGroupOrFail(groupCode) @@ -655,7 +693,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { if (isLong) { group.sendMessage( RichMessage.longMessage( - brief = message.joinToString(limit = 27) { it.message.contentToString() }, + brief = message.joinToString(limit = 27) { it.messageChain.contentToString() }, resId = resId, timeSeconds = time ) diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index dba3c9b41..bfc0886d3 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -170,7 +170,7 @@ internal class GroupImpl( ForwardMessage.Node( senderId = bot.id, time = currentTimeSeconds().toInt(), - message = chain, + messageChain = chain, senderName = bot.nick ) ), @@ -195,7 +195,7 @@ internal class GroupImpl( } val result = bot.network.runCatching { - val source: MessageSourceToGroupImpl + val source: OnlineMessageSourceToGroupImpl MessageSvcPbSendMsg.createToGroup( bot.client, this@GroupImpl, diff --git a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt index 284d03792..468200dff 100644 --- a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.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. @@ -19,7 +19,7 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* -import net.mamoe.mirai.internal.message.MessageSourceToTempImpl +import net.mamoe.mirai.internal.message.OnlineMessageSourceToTempImpl import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable import net.mamoe.mirai.internal.message.firstIsInstanceOrNull import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement @@ -81,7 +81,7 @@ internal class NormalMemberImpl constructor( chain.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() val result = bot.network.runCatching { - val source: MessageSourceToTempImpl + val source: OnlineMessageSourceToTempImpl MessageSvcPbSendMsg.createToTemp( bot.client, this@NormalMemberImpl, diff --git a/mirai-core/src/commonMain/kotlin/contact/util.kt b/mirai-core/src/commonMain/kotlin/contact/util.kt index 447a98f8a..4b9c557ec 100644 --- a/mirai-core/src/commonMain/kotlin/contact/util.kt +++ b/mirai-core/src/commonMain/kotlin/contact/util.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. @@ -16,8 +16,8 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* import net.mamoe.mirai.internal.asQQAndroidBot -import net.mamoe.mirai.internal.message.MessageSourceToFriendImpl -import net.mamoe.mirai.internal.message.MessageSourceToStrangerImpl +import net.mamoe.mirai.internal.message.OnlineMessageSourceToFriendImpl +import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend @@ -37,8 +37,8 @@ internal inline val Bot.uin: Long get() = this.id internal suspend fun Friend.sendMessageImpl( message: Message, - friendReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt, - tReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt + friendReceiptConstructor: (OnlineMessageSourceToFriendImpl) -> MessageReceipt, + tReceiptConstructor: (OnlineMessageSourceToFriendImpl) -> MessageReceipt ): MessageReceipt { contract { callsInPlace(friendReceiptConstructor, InvocationKind.EXACTLY_ONCE) } val bot = bot.asQQAndroidBot() @@ -56,7 +56,7 @@ internal suspend fun Friend.sendMessageImpl( chain.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() - lateinit var source: MessageSourceToFriendImpl + lateinit var source: OnlineMessageSourceToFriendImpl val result = bot.network.runCatching { MessageSvcPbSendMsg.createToFriend( bot.client, @@ -89,8 +89,8 @@ internal suspend fun Friend.sendMessageImpl( internal suspend fun Stranger.sendMessageImpl( message: Message, - strangerReceiptConstructor: (MessageSourceToStrangerImpl) -> MessageReceipt, - tReceiptConstructor: (MessageSourceToStrangerImpl) -> MessageReceipt + strangerReceiptConstructor: (OnlineMessageSourceToStrangerImpl) -> MessageReceipt, + tReceiptConstructor: (OnlineMessageSourceToStrangerImpl) -> MessageReceipt ): MessageReceipt { contract { callsInPlace(strangerReceiptConstructor, InvocationKind.EXACTLY_ONCE) } val bot = bot.asQQAndroidBot() @@ -108,7 +108,7 @@ internal suspend fun Stranger.sendMessageImpl( chain.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() - lateinit var source: MessageSourceToStrangerImpl + lateinit var source: OnlineMessageSourceToStrangerImpl val result = bot.network.runCatching { MessageSvcPbSendMsg.createToStranger( bot.client, diff --git a/mirai-core/src/commonMain/kotlin/message/conversions.kt b/mirai-core/src/commonMain/kotlin/message/conversions.kt index 64f115261..8592f33c2 100644 --- a/mirai-core/src/commonMain/kotlin/message/conversions.kt +++ b/mirai-core/src/commonMain/kotlin/message/conversions.kt @@ -328,13 +328,13 @@ internal fun List.toMessageChain( checkNotNull(bot) { "bot is null" } when (messageSourceKind) { - MessageSourceKind.TEMP -> +MessageSourceFromTempImpl(bot, this@toMessageChain) - MessageSourceKind.GROUP -> +MessageSourceFromGroupImpl(bot, this@toMessageChain) - MessageSourceKind.FRIEND -> +MessageSourceFromFriendImpl(bot, this@toMessageChain) - MessageSourceKind.STRANGER -> +MessageSourceFromStrangerImpl(bot, this@toMessageChain) + MessageSourceKind.TEMP -> +OnlineMessageSourceFromTempImpl(bot, this@toMessageChain) + MessageSourceKind.GROUP -> +OnlineMessageSourceFromGroupImpl(bot, this@toMessageChain) + MessageSourceKind.FRIEND -> +OnlineMessageSourceFromFriendImpl(bot, this@toMessageChain) + MessageSourceKind.STRANGER -> +OnlineMessageSourceFromStrangerImpl(bot, this@toMessageChain) } } else { - +OfflineMessageSourceImplByMsg(bot, this@toMessageChain, botId) + +OfflineMessageSourceImplData(bot, this@toMessageChain, botId) } elements.joinToMessageChain(groupIdOrZero, botId, this) addAll(ptts) @@ -348,7 +348,7 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(botId: Long, groupIdOrZero: Long if (elements.isEmpty()) error("elements for SourceMsg is empty") return buildMessageChain(elements.size + 1) { - +OfflineMessageSourceImplBySourceMsg( + +OfflineMessageSourceImplData( delegate = this@toMessageChain, botId = botId, groupIdOrZero = groupIdOrZero @@ -422,7 +422,7 @@ internal fun List.joinToMessageChain(groupIdOrZero: Long, botId: this.forEach { element -> when { element.srcMsg != null -> { - list.add(QuoteReply(OfflineMessageSourceImplBySourceMsg(element.srcMsg, botId, groupIdOrZero))) + list.add(QuoteReply(OfflineMessageSourceImplData(element.srcMsg, botId, groupIdOrZero))) } element.notOnlineImage != null -> list.add(OnlineFriendImageImpl(element.notOnlineImage)) element.customFace != null -> list.add(OnlineGroupImageImpl(element.customFace)) diff --git a/mirai-core/src/commonMain/kotlin/message/faceImpl.kt b/mirai-core/src/commonMain/kotlin/message/faceImpl.kt index 5fe2d7b3e..50386fdf3 100644 --- a/mirai-core/src/commonMain/kotlin/message/faceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/faceImpl.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. @@ -10,6 +10,7 @@ package net.mamoe.mirai.internal.message import kotlinx.io.core.toByteArray +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem @@ -43,6 +44,7 @@ internal fun Face.toCommData(): ImMsgBody.CommonElem { } +@SerialName(MarketFace.SERIAL_NAME) @Serializable internal data class MarketFaceImpl internal constructor( internal val delegate: ImMsgBody.MarketFace, diff --git a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt index f907f05af..1c0957e9d 100644 --- a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt @@ -30,10 +30,13 @@ import net.mamoe.mirai.utils.generateImageIdFromResourceId import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.toUHexString +@Serializable(with = OnlineGroupImageImpl.Serializer::class) internal class OnlineGroupImageImpl( internal val delegate: ImMsgBody.CustomFace -) : @Suppress("DEPRECATION") -OnlineGroupImage() { +) : OnlineGroupImage() { + object Serializer : Image.FallbackSerializer("OnlineGroupImage") + + override val imageId: String = generateImageId( delegate.picMd5, delegate.filePath.substringAfterLast('.') @@ -58,10 +61,13 @@ OnlineGroupImage() { } } +@Serializable(with = OnlineFriendImageImpl.Serializer::class) internal class OnlineFriendImageImpl( internal val delegate: ImMsgBody.NotOnlineImage ) : @Suppress("DEPRECATION") OnlineFriendImage() { + object Serializer : Image.FallbackSerializer("OnlineFriendImage") + override val imageId: String = generateImageIdFromResourceId(delegate.resId, getImageType(delegate.imgType)) ?: delegate.resId override val originUrl: String @@ -212,7 +218,6 @@ internal interface SuspendDeferredOriginUrlAware : Image { suspend fun getUrl(bot: Bot): String } -@Suppress("EXPOSED_SUPER_INTERFACE") internal interface OnlineImage : Image, ConstOriginUrlAware { override val originUrl: String } @@ -228,11 +233,12 @@ internal interface OfflineImage : Image /** * @param imageId 参考 [Image.imageId] */ -@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") -@Serializable +@Serializable(with = OfflineGroupImage.Serializer::class) internal data class OfflineGroupImage( override val imageId: String ) : GroupImage(), OfflineImage, DeferredOriginUrlAware { + object Serializer : Image.FallbackSerializer("OfflineGroupImage") + override fun getUrl(bot: Bot): String { return "http://gchat.qpic.cn/gchatpic_new/${bot.id}/0-0-${ imageId.substring(1..36) @@ -265,10 +271,12 @@ internal val Image.friendImageId: String * * @param imageId 参考 [Image.imageId] */ -@Serializable +@Serializable(with = OfflineFriendImage.Serializer::class) internal data class OfflineFriendImage( override val imageId: String ) : FriendImage(), OfflineImage, DeferredOriginUrlAware { + object Serializer : Image.FallbackSerializer("OfflineFriendImage") + override fun getUrl(bot: Bot): String { return "http://c2cpicdw.qpic.cn/offpic_new/${bot.id}${this.friendImageId}/0?term=2" } diff --git a/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt index c9cea71be..7ef569cc4 100644 --- a/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.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. @@ -71,10 +71,13 @@ internal suspend inline fun Message.ensureSequenceIdAvailable() { }*/ } -internal class MessageSourceFromFriendImpl( +@Serializable(OnlineMessageSourceFromFriendImpl.Serializer::class) +internal class OnlineMessageSourceFromFriendImpl( override val bot: Bot, val msg: List ) : OnlineMessageSource.Incoming.FromFriend(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromFriend") + override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq } override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) override val ids: IntArray get() = sequenceIds// msg.msgBody.richText.attr!!.random @@ -99,10 +102,13 @@ internal class MessageSourceFromFriendImpl( override fun toJceData(): ImMsgBody.SourceMsg = jceData } -internal class MessageSourceFromStrangerImpl( +@Serializable(OnlineMessageSourceFromStrangerImpl.Serializer::class) +internal class OnlineMessageSourceFromStrangerImpl( override val bot: Bot, val msg: List ) : OnlineMessageSource.Incoming.FromStranger(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromStranger") + override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq } override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) override val ids: IntArray get() = sequenceIds// msg.msgBody.richText.attr!!.random @@ -163,10 +169,13 @@ private fun List.toJceDataPrivate(ids: IntArray): ImMsgBody.SourceM ) } -internal class MessageSourceFromTempImpl( +@Serializable(OnlineMessageSourceFromTempImpl.Serializer::class) +internal class OnlineMessageSourceFromTempImpl( override val bot: Bot, private val msg: List ) : OnlineMessageSource.Incoming.FromTemp(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromTemp") + override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq } override val internalIds: IntArray get() = msg.mapToIntArray { it.msgBody.richText.attr!!.random } override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) @@ -190,12 +199,12 @@ internal class MessageSourceFromTempImpl( override fun toJceData(): ImMsgBody.SourceMsg = jceData } -@Serializable(MessageSourceFromGroupImpl.Serializer::class) -internal data class MessageSourceFromGroupImpl( +@Serializable(OnlineMessageSourceFromGroupImpl.Serializer::class) +internal data class OnlineMessageSourceFromGroupImpl( override val bot: Bot, private val msg: List ) : OnlineMessageSource.Incoming.FromGroup(), MessageSourceInternal { - object Serializer : MessageSourceSerializerImpl("net.mamoe.mirai.internal.message.MessageSourceFromGroupImpl") + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromGroupImpl") @Transient override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) diff --git a/mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.kt index 87e5d1116..4cc5a26e2 100644 --- a/mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.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. @@ -17,15 +17,15 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg -import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.internal.utils.io.serialization.loadAs +import net.mamoe.mirai.message.MessageSourceSerializerImpl import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.message.data.OfflineMessageSource import net.mamoe.mirai.utils.mapToIntArray import java.util.concurrent.atomic.AtomicBoolean -@Serializable +@Serializable(OfflineMessageSourceImplData.Serializer::class) internal data class OfflineMessageSourceImplData( override val kind: MessageSourceKind, override val ids: IntArray, @@ -36,19 +36,30 @@ internal data class OfflineMessageSourceImplData( override val originalMessage: MessageChain, override val internalIds: IntArray, ) : OfflineMessageSource(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OfflineMessageSource") + override val sequenceIds: IntArray get() = ids + // for override. + // if provided, no need to serialize from message + @Transient + var originElems: List? = null + + // may provided by OfflineMessageSourceImplBySourceMsg + @Transient + var jceData: ImMsgBody.SourceMsg? = null + @Transient @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) override fun toJceData(): ImMsgBody.SourceMsg { - return ImMsgBody.SourceMsg( + return jceData ?: ImMsgBody.SourceMsg( origSeqs = sequenceIds, senderUin = fromId, toUin = 0, flag = 1, - elems = originalMessage.toRichTextElems( + elems = originElems ?: originalMessage.toRichTextElems( null, //forGroup = kind == MessageSourceKind.GROUP, withGeneralFlags = false ), @@ -56,7 +67,7 @@ internal data class OfflineMessageSourceImplData( time = time, pbReserve = net.mamoe.mirai.internal.EMPTY_BYTE_ARRAY, srcMsg = net.mamoe.mirai.internal.EMPTY_BYTE_ARRAY - ) + ).also { jceData = it } } override fun equals(other: Any?): Boolean { @@ -90,92 +101,60 @@ internal data class OfflineMessageSourceImplData( } } -internal class OfflineMessageSourceImplByMsg( - // from other sources' originalMessage +internal fun OfflineMessageSourceImplData( bot: Bot?, - val delegate: List, - override val botId: Long, -) : OfflineMessageSource(), MessageSourceInternal { - override val kind: MessageSourceKind = - when { - delegate.first().msgHead.groupInfo != null -> { - MessageSourceKind.GROUP - } - delegate.first().msgHead.c2cTmpMsgHead != null -> { - MessageSourceKind.TEMP - } - bot?.getStranger(delegate.first().msgHead.fromUin) != null -> { - MessageSourceKind.STRANGER - } - else -> { - MessageSourceKind.FRIEND - } + delegate: List, + botId: Long, +): OfflineMessageSourceImplData { + val kind = when { + delegate.first().msgHead.groupInfo != null -> { + MessageSourceKind.GROUP } - override val ids: IntArray get() = sequenceIds - override val internalIds: IntArray = delegate.mapToIntArray { it.msgHead.msgUid.toInt() } - override val time: Int - get() = delegate.first().msgHead.msgTime - override val fromId: Long - get() = delegate.first().msgHead.fromUin - override val targetId: Long - get() = delegate.first().msgHead.groupInfo?.groupCode ?: delegate.first().msgHead.toUin - override val originalMessage: MessageChain by lazy { - delegate.toMessageChain( + delegate.first().msgHead.c2cTmpMsgHead != null -> { + MessageSourceKind.TEMP + } + bot?.getStranger(delegate.first().msgHead.fromUin) != null -> { + MessageSourceKind.STRANGER + } + else -> { + MessageSourceKind.FRIEND + } + } + return OfflineMessageSourceImplData( + kind = kind, + time = delegate.first().msgHead.msgTime, + fromId = delegate.first().msgHead.fromUin, + targetId = delegate.first().msgHead.groupInfo?.groupCode ?: delegate.first().msgHead.toUin, + originalMessage = delegate.toMessageChain( null, botId, groupIdOrZero = delegate.first().msgHead.groupInfo?.groupCode ?: 0, onlineSource = false, messageSourceKind = kind - ) - } - override val sequenceIds: IntArray = delegate.mapToIntArray { it.msgHead.msgSeq } - - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) - - override fun toJceData(): ImMsgBody.SourceMsg { - return ImMsgBody.SourceMsg( - origSeqs = delegate.mapToIntArray { it.msgHead.msgSeq }, - senderUin = delegate.first().msgHead.fromUin, - toUin = 0, - flag = 1, - elems = delegate.flatMap { it.msgBody.richText.elems }, - type = 0, - time = delegate.first().msgHead.msgTime, - pbReserve = EMPTY_BYTE_ARRAY, - srcMsg = EMPTY_BYTE_ARRAY - ) + ), + ids = delegate.mapToIntArray { it.msgHead.msgSeq }, + internalIds = delegate.mapToIntArray { it.msgHead.msgUid.toInt() }, + botId = botId + ).apply { + originElems = delegate.flatMap { it.msgBody.richText.elems } } } -internal class OfflineMessageSourceImplBySourceMsg( - // from others' quotation - val delegate: ImMsgBody.SourceMsg, - override val botId: Long, - groupIdOrZero: Long -) : OfflineMessageSource(), MessageSourceInternal { - override val kind: MessageSourceKind get() = if (delegate.srcMsg == null) MessageSourceKind.GROUP else MessageSourceKind.FRIEND - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) - override val sequenceIds: IntArray = delegate.origSeqs - override val internalIds: IntArray = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()) - .origUids?.mapToIntArray { it.toInt() } ?: intArrayOf() - override val time: Int get() = delegate.time - override val originalMessage: MessageChain by lazy { delegate.toMessageChain(botId, groupIdOrZero) } - /* - override val ids: Long - get() = (delegate.origSeqs?.firstOrNull() - ?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or - delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF) - */ - - override val ids: IntArray get() = sequenceIds - // delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids?.toInt() - // ?: 0 - - // override val sourceMessage: MessageChain get() = delegate.toMessageChain() - override val fromId: Long get() = delegate.senderUin - override val targetId: Long by lazy { - when { +internal fun OfflineMessageSourceImplData( + delegate: ImMsgBody.SourceMsg, + botId: Long, + groupIdOrZero: Long, +): OfflineMessageSourceImplData { + return OfflineMessageSourceImplData( + kind = if (delegate.srcMsg == null) MessageSourceKind.GROUP else MessageSourceKind.FRIEND, + ids = delegate.origSeqs, + internalIds = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()) + .origUids?.mapToIntArray { it.toInt() } ?: intArrayOf(), + time = delegate.time, + originalMessage = delegate.toMessageChain(botId, groupIdOrZero), + fromId = delegate.senderUin, + targetId = when { groupIdOrZero != 0L -> groupIdOrZero delegate.toUin != 0L -> delegate.toUin delegate.srcMsg != null -> delegate.srcMsg.loadAs(MsgComm.Msg.serializer()).msgHead.toUin @@ -187,10 +166,9 @@ internal class OfflineMessageSourceImplBySourceMsg( ) }" )*/ - } - } - - override fun toJceData(): ImMsgBody.SourceMsg { - return delegate + }, + botId = botId + ).apply { + jceData = delegate } } diff --git a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt index cc0e37395..e0d68c432 100644 --- a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.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. @@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.message import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.serialization.Serializable import net.mamoe.mirai.Bot import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.* @@ -24,6 +25,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushPbPushGroupMsg.SendGroupMessageReceipt import net.mamoe.mirai.internal.network.protocol.packet.chat.toLongUnsigned import net.mamoe.mirai.internal.utils.io.serialization.toByteArray +import net.mamoe.mirai.message.MessageSourceSerializerImpl import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.OnlineMessageSource @@ -72,7 +74,8 @@ private fun T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg ) } -internal class MessageSourceToFriendImpl( +@Serializable(OnlineMessageSourceToFriendImpl.Serializer::class) +internal class OnlineMessageSourceToFriendImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, override val time: Int, @@ -80,6 +83,8 @@ internal class MessageSourceToFriendImpl( override val sender: Bot, override val target: Friend ) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToFriend") + override val bot: Bot get() = sender override val ids: IntArray @@ -89,7 +94,8 @@ internal class MessageSourceToFriendImpl( override fun toJceData(): ImMsgBody.SourceMsg = jceData } -internal class MessageSourceToStrangerImpl( +@Serializable(OnlineMessageSourceToStrangerImpl.Serializer::class) +internal class OnlineMessageSourceToStrangerImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, override val time: Int, @@ -97,6 +103,8 @@ internal class MessageSourceToStrangerImpl( override val sender: Bot, override val target: Stranger ) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger") + override val bot: Bot get() = sender override val ids: IntArray @@ -106,7 +114,8 @@ internal class MessageSourceToStrangerImpl( override fun toJceData(): ImMsgBody.SourceMsg = jceData } -internal class MessageSourceToTempImpl( +@Serializable(OnlineMessageSourceToTempImpl.Serializer::class) +internal class OnlineMessageSourceToTempImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, override val time: Int, @@ -114,6 +123,8 @@ internal class MessageSourceToTempImpl( override val sender: Bot, override val target: Member ) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp") + override val bot: Bot get() = sender override val ids: IntArray @@ -123,7 +134,8 @@ internal class MessageSourceToTempImpl( override fun toJceData(): ImMsgBody.SourceMsg = jceData } -internal class MessageSourceToGroupImpl( +@Serializable(OnlineMessageSourceToGroupImpl.Serializer::class) +internal class OnlineMessageSourceToGroupImpl( coroutineScope: CoroutineScope, override val internalIds: IntArray, override val time: Int, @@ -131,6 +143,8 @@ internal class MessageSourceToGroupImpl( override val sender: Bot, override val target: Group ) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal { + object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToGroup") + override val ids: IntArray get() = sequenceIds override val bot: Bot @@ -141,7 +155,7 @@ internal class MessageSourceToGroupImpl( coroutineScope.asyncFromEventOrNull( timeoutMillis = 3000 ) { - if (it.messageRandom in this@MessageSourceToGroupImpl.internalIds) { + if (it.messageRandom in this@OnlineMessageSourceToGroupImpl.internalIds) { it.sequenceId } else null } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt index 78ea7247a..9f7e02d82 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.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. @@ -71,7 +71,7 @@ internal fun Collection.calculateValidationDataForGroup( ), msgBody = ImMsgBody.MsgBody( richText = ImMsgBody.RichText( - elems = chain.message.asMessageChain() + elems = chain.messageChain.asMessageChain() .toRichTextElems(targetGroup, withGeneralFlags = false).toMutableList() ) ) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt index c6491e1e5..f52c58135 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.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. @@ -17,10 +17,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.sync.withLock -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.discardExact -import kotlinx.io.core.readUByte -import kotlinx.io.core.readUShort import kotlinx.io.core.* import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.Group @@ -34,7 +30,7 @@ import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.contact.* -import net.mamoe.mirai.internal.message.MessageSourceFromFriendImpl +import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl import net.mamoe.mirai.internal.message.toMessageChain import net.mamoe.mirai.internal.network.MultiPacket import net.mamoe.mirai.internal.network.Packet @@ -437,7 +433,7 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot): Packet? { ?: return null// don't compare with dstAppId. diff. val chain = buildMessageChain { - +MessageSourceFromFriendImpl(bot, listOf(this@transform)) + +OnlineMessageSourceFromFriendImpl(bot, listOf(this@transform)) for (msgItem in textMsg.msgItems) { when (msgItem.type) { 1 -> +PlainText(msgItem.text) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt index e22ec36cb..3585c5280 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.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. @@ -143,7 +143,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit + crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit ): List { contract { callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) @@ -209,7 +209,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit + crossinline sourceCallback: (OnlineMessageSourceToTempImpl) -> Unit ): OutgoingPacket { contract { callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) } - val source = MessageSourceToTempImpl( + val source = OnlineMessageSourceToTempImpl( internalIds = intArrayOf(Random.nextInt().absoluteValue), sender = client.bot, target = member, @@ -380,12 +380,12 @@ internal inline fun MessageSvcPbSendMsg.createToStranger( client: QQAndroidClient, stranger: Stranger, message: MessageChain, - crossinline sourceCallback: (MessageSourceToStrangerImpl) -> Unit + crossinline sourceCallback: (OnlineMessageSourceToStrangerImpl) -> Unit ): OutgoingPacket { contract { callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) } - val source = MessageSourceToStrangerImpl( + val source = OnlineMessageSourceToStrangerImpl( internalIds = intArrayOf(Random.nextInt().absoluteValue), sender = client.bot, target = stranger, @@ -406,7 +406,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend( client: QQAndroidClient, qq: Friend, message: MessageChain, - crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit + crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit ): List { contract { callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) @@ -425,13 +425,13 @@ internal inline fun MessageSvcPbSendMsg.createToGroup( group: Group, message: MessageChain, isForward: Boolean, - crossinline sourceCallback: (MessageSourceToGroupImpl) -> Unit + crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit ): OutgoingPacket { contract { callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) } val messageRandom = Random.nextInt().absoluteValue - val source = MessageSourceToGroupImpl( + val source = OnlineMessageSourceToGroupImpl( group, internalIds = intArrayOf(messageRandom), sender = client.bot, diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt index 208159fee..5dc6dfd26 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.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. @@ -15,6 +15,7 @@ import kotlinx.serialization.serializer import net.mamoe.mirai.Mirai import net.mamoe.mirai.internal.message.MarketFaceImpl import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.message.MessageSerializer import net.mamoe.mirai.message.data.* import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test @@ -22,11 +23,13 @@ import kotlin.test.assertEquals internal class MessageSerializationTest { @Suppress("DEPRECATION_ERROR") - private val module get() = Message.Serializer.serializersModule + private val module + get() = MessageSerializer.serializersModule private val format get() = Json { serializersModule = module useArrayPolymorphism = false // ? + ignoreUnknownKeys = true } private inline fun T.serialize(serializer: KSerializer = module.serializer()): String { @@ -38,7 +41,14 @@ internal class MessageSerializationTest { } private inline fun testSerialization(t: T, serializer: KSerializer = module.serializer()) { - val deserialized = t.serialize(serializer).deserialize(serializer) + val deserialized = kotlin.runCatching { + println("Testing ${t::class.simpleName} with serializer $serializer") + val serialized = t.serialize(serializer) + println("Result: ${serializer.descriptor.serialName} $serialized") + serialized.deserialize(serializer) + }.getOrElse { + throw AssertionError("Failed to serialize $t", it) + } assertEquals( t, deserialized, @@ -78,7 +88,6 @@ internal class MessageSerializationTest { @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") private val testConstrainSingleMessageInstances: Array = arrayOf( - LongMessage("content", "resId"), Mirai.constructMessageSource( 1L, MessageSourceKind.FRIEND, @@ -100,7 +109,12 @@ internal class MessageSerializationTest { } @Test - fun `test serialize each message contents`() { + fun `test polymorphic serialization`() { + + } + + @Test + fun `test contextual serialization`() { for (message in testMessageContentInstances) { testSerialization(message, module.serializer(message.javaClass)) }