Karlatemp 3 лет назад
Родитель
Сommit
1ea68d82a7

+ 7 - 0
mirai-core-mock/src/internal/MockMiraiImpl.kt

@@ -31,11 +31,18 @@ import net.mamoe.mirai.mock.internal.contact.AQQ_RECALL_FAILED_MESSAGE
 import net.mamoe.mirai.mock.internal.contact.MockFriendImpl
 import net.mamoe.mirai.mock.internal.contact.MockImage
 import net.mamoe.mirai.mock.internal.contact.MockStrangerImpl
+import net.mamoe.mirai.mock.internal.msgsrc.registerMockMsgSerializers
 import net.mamoe.mirai.mock.utils.mock
 import net.mamoe.mirai.mock.utils.simpleMemberInfo
 import net.mamoe.mirai.utils.currentTimeSeconds
 
 internal class MockMiraiImpl : MiraiImpl() {
+    companion object {
+        init {
+            registerMockMsgSerializers()
+        }
+    }
+
     override suspend fun solveBotInvitedJoinGroupRequestEvent(
         bot: Bot,
         eventId: Long,

+ 5 - 0
mirai-core-mock/src/internal/contact/util.kt

@@ -11,6 +11,7 @@
 
 package net.mamoe.mirai.mock.internal.contact
 
+import kotlinx.serialization.Serializable
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
@@ -95,6 +96,8 @@ internal suspend fun ExternalResource.mockImplUploadAudioAsOnline(bot: MockBot):
     )
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(MockImage.Serializer::class)
 internal class MockImage(
     override val imageId: String,
     private val urlPath: String,
@@ -112,6 +115,8 @@ internal class MockImage(
         }
     }
 
+    object Serializer : Image.FallbackSerializer("MockImage")
+
     private val _stringValue: String? by lazy(LazyThreadSafetyMode.NONE) { "[mirai:image:$imageId]" }
 
     override fun getUrl(bot: Bot): String {

+ 121 - 13
mirai-core-mock/src/internal/msgsrc/OnlineMsgSrc.kt

@@ -7,16 +7,98 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
 package net.mamoe.mirai.mock.internal.msgsrc
 
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.message.data.MessageSourceKind
-import net.mamoe.mirai.message.data.OnlineMessageSource
+import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl
+import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacadeImpl
+import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer
+import net.mamoe.mirai.message.MessageSerializers
+import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.mock.internal.contact.AbstractMockContact
+import net.mamoe.mirai.mock.internal.contact.MockImage
 import net.mamoe.mirai.utils.currentTimeSeconds
 
+internal fun registerMockMsgSerializers() {
+    val serializers = mutableListOf<MessageSerializer<*>>()
+
+    MessageSerializer.superclassesScope(Image::class, MessageContent::class, SingleMessage::class) {
+        serializers.add(
+            MessageSerializer(
+                MockImage::class,
+                MockImage.serializer()
+            )
+        )
+    }
+
+    MessageSerializer.superclassesScope(MessageSource::class, MessageMetadata::class, SingleMessage::class) {
+
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcToGroup::class,
+                OnlineMsgSrcToGroup.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcToFriend::class,
+                OnlineMsgSrcToFriend.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcToStranger::class,
+                OnlineMsgSrcToStranger.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcToTemp::class,
+                OnlineMsgSrcToTemp.serializer()
+            )
+        )
+
+
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcFromGroup::class,
+                OnlineMsgSrcFromGroup.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcFromFriend::class,
+                OnlineMsgSrcFromFriend.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcFromStranger::class,
+                OnlineMsgSrcFromStranger.serializer()
+            )
+        )
+        serializers.add(
+            MessageSerializer(
+                OnlineMsgSrcFromTemp::class,
+                OnlineMsgSrcFromTemp.serializer()
+            )
+        )
+    }
+
+    val module = MessageProtocolFacadeImpl(listOf(), "").also {
+        it.serializers.addAll(serializers)
+    }.createSerializersModule()
+
+    MessageSerializers.registerSerializers(module)
+}
+
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcToGroup.Serializer::class)
 internal class OnlineMsgSrcToGroup(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -27,8 +109,13 @@ internal class OnlineMsgSrcToGroup(
     override val target: Group
 ) : OnlineMessageSource.Outgoing.ToGroup() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
+
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcToFriend.Serializer::class)
 internal class OnlineMsgSrcToFriend(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -39,8 +126,14 @@ internal class OnlineMsgSrcToFriend(
     override val target: Friend
 ) : OnlineMessageSource.Outgoing.ToFriend() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
+
 }
 
+
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcToStranger.Serializer::class)
 internal class OnlineMsgSrcToStranger(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -51,8 +144,12 @@ internal class OnlineMsgSrcToStranger(
     override val target: Stranger
 ) : OnlineMessageSource.Outgoing.ToStranger() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcToTemp.Serializer::class)
 internal class OnlineMsgSrcToTemp(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -63,19 +160,13 @@ internal class OnlineMsgSrcToTemp(
     override val target: Member
 ) : OnlineMessageSource.Outgoing.ToTemp() {
     override val isOriginalMessageInitialized: Boolean get() = true
-}
 
-internal class OnlineMsgFromGroup(
-    override val ids: IntArray,
-    override val internalIds: IntArray,
-    override val time: Int,
-    override val originalMessage: MessageChain,
-    override val bot: Bot,
-    override val sender: Member
-) : OnlineMessageSource.Incoming.FromGroup() {
-    override val isOriginalMessageInitialized: Boolean get() = true
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
 }
 
+
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcFromFriend.Serializer::class)
 internal class OnlineMsgSrcFromFriend(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -85,8 +176,12 @@ internal class OnlineMsgSrcFromFriend(
     override val sender: Friend
 ) : OnlineMessageSource.Incoming.FromFriend() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromFriend")
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcFromStranger.Serializer::class)
 internal class OnlineMsgSrcFromStranger(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -96,8 +191,12 @@ internal class OnlineMsgSrcFromStranger(
     override val sender: Stranger
 ) : OnlineMessageSource.Incoming.FromStranger() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromStranger")
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcFromTemp.Serializer::class)
 internal class OnlineMsgSrcFromTemp(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -107,8 +206,13 @@ internal class OnlineMsgSrcFromTemp(
     override val sender: Member
 ) : OnlineMessageSource.Incoming.FromTemp() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromTemp")
+
 }
 
+@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
+@Serializable(OnlineMsgSrcFromGroup.Serializer::class)
 internal class OnlineMsgSrcFromGroup(
     override val ids: IntArray,
     override val internalIds: IntArray,
@@ -118,6 +222,9 @@ internal class OnlineMsgSrcFromGroup(
     override val sender: Member
 ) : OnlineMessageSource.Incoming.FromGroup() {
     override val isOriginalMessageInitialized: Boolean get() = true
+
+    object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromGroup")
+
 }
 
 internal typealias MsgSrcConstructor<R> = (
@@ -141,6 +248,7 @@ internal inline fun <R> AbstractMockContact.newMsgSrc(
                 is Stranger,
                 is Friend,
                 -> this.id
+
                 else -> error("Invalid contact: $this")
             },
             kind = when (this) {

+ 123 - 0
mirai-core-mock/test/mock/MessageSerializationTest.kt

@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019-2022 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/dev/LICENSE
+ */
+
+package net.mamoe.mirai.mock.test.mock
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.serializer
+import net.mamoe.mirai.message.MessageSerializers
+import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.mock.test.MockBotTestBase
+import net.mamoe.mirai.mock.utils.randomImageContent
+import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+
+internal class MessageSerializationTest : MockBotTestBase() {
+    @Suppress("DEPRECATION_ERROR")
+    private val module
+        get() = MessageSerializers.serializersModule
+    private val format
+        get() = Json {
+            serializersModule = module
+            useArrayPolymorphism = false
+            ignoreUnknownKeys = true
+        }
+
+    private inline fun <reified T : Any> T.serialize(serializer: KSerializer<T> = module.serializer()): String {
+        return format.encodeToString(serializer, this)
+    }
+
+    private inline fun <reified T : Any> String.deserialize(serializer: KSerializer<T> = module.serializer()): T {
+        return format.decodeFromString(serializer, this)
+    }
+
+    private inline fun <reified T : Any> testSerialization(t: T, serializer: KSerializer<T> = module.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 IllegalStateException("Failed to serialize $t", it)
+        }
+
+        val msg = "serialized string:   ${t.serialize(serializer)}\ndeserialized string: ${
+            deserialized.serialize(
+                serializer
+            )
+        }\n"
+
+
+
+        assert1(
+            t,
+            deserialized,
+            msg
+        )
+    }
+
+    private fun assert1(t: Any, deserialized: Any, msg: String) {
+        if (deserialized is MessageSource && t is MessageSource) {
+            assertSource(t, deserialized, msg)
+            return
+        }
+
+        if (t is MessageChain && deserialized is MessageChain) {
+            assertEquals(t.size, deserialized.size)
+            val iter1 = t.iterator()
+            val iter2 = deserialized.iterator()
+
+            repeat(t.size) {
+                assert1(iter1.next(), iter2.next(), msg)
+            }
+            assertFalse(iter1.hasNext(), msg)
+            assertFalse(iter2.hasNext(), msg)
+            return
+        }
+
+        assertEquals(t, deserialized, msg)
+    }
+
+    private fun assertSource(t: MessageSource, deserialized: MessageSource, msg: String) {
+        assertEquals(t.kind, deserialized.kind, msg)
+        assertEquals(t.botId, deserialized.botId, msg)
+        assertEquals(t.fromId, deserialized.fromId, msg)
+        assertEquals(t.targetId, deserialized.targetId, msg)
+        assertEquals(t.time, deserialized.time, msg)
+        Assertions.assertArrayEquals(t.ids, deserialized.ids, msg)
+        Assertions.assertArrayEquals(t.internalIds, deserialized.internalIds, msg)
+        assertEquals(t.originalMessage, deserialized.originalMessage, msg)
+    }
+
+
+    @Test
+    fun testMockMessageSources() = runTest {
+        testSerialization(bot.addFriend(1, "").says(""))
+        testSerialization(bot.addStranger(2, "").says(""))
+        bot.addGroup(3, "").let { group ->
+            group.sendMessage("AWA").source.let { testSerialization(messageChainOf(it)) }
+            group.addMember(6, "").says("A").let { testSerialization(it) }
+        }
+    }
+
+    @Test
+    fun testMockResources() = runTest {
+        testSerialization(bot.uploadMockImage(Image.randomImageContent().toExternalResource().toAutoCloseable()))
+
+        "1".toByteArray().toExternalResource().use { data0 ->
+            testSerialization(bot.uploadOnlineAudio(data0))
+            testSerialization(bot.asFriend.uploadAudio(data0))
+        }
+    }
+
+}