Explorar o código

Support UnsupportedMessage (#1112)

* Support UnsupportedMessage

* Fix serialize

* `UnsupportedMessage.contentToString`: use `struct.contentHashCode()`

Co-authored-by: Karlatemp <[email protected]>
Him188 %!s(int64=5) %!d(string=hai) anos
pai
achega
430c2c59ad

+ 24 - 0
binary-compatibility-validator/android/api/binary-compatibility-validator-android.api

@@ -90,6 +90,7 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
 	public abstract fun constructMessageSource (JLnet/mamoe/mirai/message/data/MessageSourceKind;JJ[II[ILnet/mamoe/mirai/message/data/MessageChain;)Lnet/mamoe/mirai/message/data/OfflineMessageSource;
 	public abstract fun createFileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
 	public abstract fun createImage (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
+	public abstract fun createUnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
 	public fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Ljava/util/List;
 	public abstract fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/MessageChain;
@@ -4727,6 +4728,7 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
 	public static final synthetic fun At (Lnet/mamoe/mirai/contact/UserOrBot;)Lnet/mamoe/mirai/message/data/At;
 	public static final synthetic fun FileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
 	public static final synthetic fun Image (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
+	public static final synthetic fun UnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
 	public static final synthetic fun at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
 	public static final fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
 	public static final fun buildMessageChain (Lkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
@@ -5258,6 +5260,28 @@ public final class net/mamoe/mirai/message/data/SingleMessage$Serializer : kotli
 	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/SingleMessage;)V
 }
 
+public abstract interface class net/mamoe/mirai/message/data/UnsupportedMessage : net/mamoe/mirai/message/data/MessageContent {
+	public static final field Companion Lnet/mamoe/mirai/message/data/UnsupportedMessage$Companion;
+	public static final field SERIAL_NAME Ljava/lang/String;
+	public fun contentToString ()Ljava/lang/String;
+	public static fun create ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+	public abstract fun getStruct ()[B
+}
+
+public final class net/mamoe/mirai/message/data/UnsupportedMessage$Companion {
+	public static final field SERIAL_NAME Ljava/lang/String;
+	public final fun create ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+}
+
+public final class net/mamoe/mirai/message/data/UnsupportedMessage$Serializer : kotlinx/serialization/KSerializer {
+	public static final field INSTANCE Lnet/mamoe/mirai/message/data/UnsupportedMessage$Serializer;
+	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/UnsupportedMessage;)V
+}
+
 public final class net/mamoe/mirai/message/data/VipFace : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/HummerMessage {
 	public static final field AiXin Lnet/mamoe/mirai/message/data/VipFace$Kind;
 	public static final field BianBian Lnet/mamoe/mirai/message/data/VipFace$Kind;

+ 24 - 0
binary-compatibility-validator/api/binary-compatibility-validator.api

@@ -90,6 +90,7 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
 	public abstract fun constructMessageSource (JLnet/mamoe/mirai/message/data/MessageSourceKind;JJ[II[ILnet/mamoe/mirai/message/data/MessageChain;)Lnet/mamoe/mirai/message/data/OfflineMessageSource;
 	public abstract fun createFileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
 	public abstract fun createImage (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
+	public abstract fun createUnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
 	public fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Ljava/util/List;
 	public abstract fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/MessageChain;
@@ -4727,6 +4728,7 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
 	public static final synthetic fun At (Lnet/mamoe/mirai/contact/UserOrBot;)Lnet/mamoe/mirai/message/data/At;
 	public static final synthetic fun FileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
 	public static final synthetic fun Image (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
+	public static final synthetic fun UnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
 	public static final synthetic fun at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
 	public static final fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
 	public static final fun buildMessageChain (Lkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
@@ -5258,6 +5260,28 @@ public final class net/mamoe/mirai/message/data/SingleMessage$Serializer : kotli
 	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/SingleMessage;)V
 }
 
+public abstract interface class net/mamoe/mirai/message/data/UnsupportedMessage : net/mamoe/mirai/message/data/MessageContent {
+	public static final field Companion Lnet/mamoe/mirai/message/data/UnsupportedMessage$Companion;
+	public static final field SERIAL_NAME Ljava/lang/String;
+	public fun contentToString ()Ljava/lang/String;
+	public static fun create ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+	public abstract fun getStruct ()[B
+}
+
+public final class net/mamoe/mirai/message/data/UnsupportedMessage$Companion {
+	public static final field SERIAL_NAME Ljava/lang/String;
+	public final fun create ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+}
+
+public final class net/mamoe/mirai/message/data/UnsupportedMessage$Serializer : kotlinx/serialization/KSerializer {
+	public static final field INSTANCE Lnet/mamoe/mirai/message/data/UnsupportedMessage$Serializer;
+	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
+	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/UnsupportedMessage;)V
+}
+
 public final class net/mamoe/mirai/message/data/VipFace : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/HummerMessage {
 	public static final field AiXin Lnet/mamoe/mirai/message/data/VipFace$Kind;
 	public static final field BianBian Lnet/mamoe/mirai/message/data/VipFace$Kind;

+ 6 - 0
mirai-core-api/src/commonMain/kotlin/IMirai.kt

@@ -145,6 +145,12 @@ public interface IMirai : LowLevelApiAccessor {
      */
     public fun createFileMessage(id: String, internalId: Int, name: String, size: Long): FileMessage
 
+    /**
+     * 创建 [UnsupportedMessage]
+     * @since 2.6
+     */
+    public fun createUnsupportedMessage(struct: ByteArray): UnsupportedMessage
+
     /**
      * 获取图片下载链接
      *

+ 1 - 0
mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt

@@ -130,6 +130,7 @@ private val builtInSerializersModule by lazy {
             subclass(MusicShare::class, MusicShare.serializer())
 
             subclass(Dice::class, Dice.serializer())
+            subclass(UnsupportedMessage::class, UnsupportedMessage.Serializer)
         }
 
 

+ 74 - 0
mirai-core-api/src/commonMain/kotlin/message/data/UnsupportedMessage.kt

@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ *  https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+
+@file:Suppress("NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("MessageUtils")
+
+package net.mamoe.mirai.message.data
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import net.mamoe.mirai.IMirai
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.internal.message.map
+import net.mamoe.mirai.utils.chunkedHexToBytes
+import net.mamoe.mirai.utils.copy
+import net.mamoe.mirai.utils.toUHexString
+
+/**
+ * mirai 尚未支持的消息类型.
+ *
+ * [UnsupportedMessage] 可以发送, 接收, 或序列化保存
+ * @since 2.6
+ */
+@SerialName(UnsupportedMessage.SERIAL_NAME)
+@Serializable(UnsupportedMessage.Serializer::class)
+public interface UnsupportedMessage : MessageContent {
+    override fun contentToString(): String = "[不支持的消息#${struct.contentHashCode()}]" // to produce 'stable' and reliable text
+
+    /**
+     * 原生消息数据
+     */
+    public val struct: ByteArray
+
+    public companion object {
+        public const val SERIAL_NAME: String = "UnsupportedMessage"
+
+        /**
+         * 创建 [UnsupportedMessage]
+         * @see IMirai.createUnsupportedMessage
+         */
+        @JvmStatic
+        public fun create(struct: ByteArray): UnsupportedMessage = Mirai.createUnsupportedMessage(struct)
+    }
+
+    public object Serializer : KSerializer<UnsupportedMessage> by Surrogate.serializer().map(
+        resultantDescriptor = Surrogate.serializer().descriptor.copy(SERIAL_NAME),
+        deserialize = { Mirai.createUnsupportedMessage(struct.chunkedHexToBytes()) },
+        serialize = { Surrogate(struct.toUHexString("")) }
+    ) {
+        @Suppress("RemoveRedundantQualifierName")
+        @Serializable
+        @SerialName(UnsupportedMessage.SERIAL_NAME)
+        private class Surrogate(
+            val struct: String // hex
+        )
+    }
+}
+
+/**
+ * 创建 [UnsupportedMessage]
+ * @since 2.6
+ * @see UnsupportedMessage.create
+ */
+@JvmSynthetic
+public inline fun UnsupportedMessage(struct: ByteArray): UnsupportedMessage = UnsupportedMessage.create(struct)

+ 8 - 0
mirai-core/src/commonMain/kotlin/MiraiImpl.kt

@@ -30,6 +30,7 @@ import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
 import net.mamoe.mirai.internal.message.*
 import net.mamoe.mirai.internal.network.highway.*
 import net.mamoe.mirai.internal.network.protocol.data.jce.SvcDevLoginInfo
+import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
 import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit
 import net.mamoe.mirai.internal.network.protocol.packet.chat.*
@@ -109,6 +110,10 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
                 OfflineMessageSourceImplData::class,
                 OfflineMessageSourceImplData.serializer()
             )
+            MessageSerializers.registerSerializer(
+                UnsupportedMessageImpl::class,
+                UnsupportedMessageImpl.serializer()
+            )
         }
     }
 
@@ -903,6 +908,9 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
         return FileMessageImpl(id, internalId, name, size)
     }
 
+    override fun createUnsupportedMessage(struct: ByteArray): UnsupportedMessage =
+        UnsupportedMessageImpl(struct.loadAs(ImMsgBody.Elem.serializer()))
+
     @Suppress("DEPRECATION", "OverridingDeprecatedMember")
     override suspend fun queryImageUrl(bot: Bot, image: Image): String = when (image) {
         is ConstOriginUrlAware -> image.originUrl

+ 1 - 0
mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt

@@ -154,6 +154,7 @@ private object ReceiveMessageTransformer {
                 // ignore
             }
             else -> {
+                builder.add(UnsupportedMessageImpl(element))
                 // println(it._miraiContentToString())
             }
         }

+ 53 - 0
mirai-core/src/commonMain/kotlin/message/UnsupportedMessageImpl.kt

@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ *  https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+
+package net.mamoe.mirai.internal.message
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
+import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
+import net.mamoe.mirai.message.data.UnsupportedMessage
+import net.mamoe.mirai.message.data.content
+import net.mamoe.mirai.utils.copy
+
+@SerialName(UnsupportedMessage.SERIAL_NAME)
+@Serializable(UnsupportedMessageImpl.Serializer::class)
+internal data class UnsupportedMessageImpl(
+    val structElem: ImMsgBody.Elem
+) : UnsupportedMessage {
+    override val struct: ByteArray by lazy { structElem.toByteArray(ImMsgBody.Elem.serializer()) }
+    override fun toString(): String = content
+    override fun hashCode(): Int {
+        return struct.contentHashCode()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (other === this) return true
+        if (other !is UnsupportedMessageImpl) return false
+        if (other.structElem == this.structElem) return true
+        return other.struct.contentEquals(this.struct)
+    }
+
+    object Serializer : KSerializer<UnsupportedMessageImpl> {
+        override val descriptor: SerialDescriptor =
+            UnsupportedMessage.Serializer.descriptor.copy(UnsupportedMessage.SERIAL_NAME)
+
+        override fun deserialize(decoder: Decoder): UnsupportedMessageImpl =
+            UnsupportedMessage.Serializer.deserialize(decoder) as UnsupportedMessageImpl
+
+        override fun serialize(encoder: Encoder, value: UnsupportedMessageImpl) =
+            UnsupportedMessage.Serializer.serialize(encoder, value)
+    }
+}

+ 1 - 0
mirai-core/src/commonMain/kotlin/message/messageToElems.kt

@@ -239,6 +239,7 @@ internal fun MessageChain.toRichTextElems(
             is InternalFlagOnlyMessage, is ShowImageFlag -> {
                 // ignore
             }
+            is UnsupportedMessageImpl -> elements.add(currentMessage.structElem)
             else -> {
                 // unrecognized types are ignored
                 // error("unsupported message type: ${currentMessage::class.simpleName}")

+ 2 - 0
mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt

@@ -19,6 +19,7 @@ import kotlinx.serialization.serializer
 import net.mamoe.mirai.Mirai
 import net.mamoe.mirai.internal.message.FileMessageImpl
 import net.mamoe.mirai.internal.message.MarketFaceImpl
+import net.mamoe.mirai.internal.message.UnsupportedMessageImpl
 import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
 import net.mamoe.mirai.message.MessageSerializers
 import net.mamoe.mirai.message.data.*
@@ -75,6 +76,7 @@ internal class MessageSerializationTest {
         AtAll,
         image,
         Face(Face.AI_NI),
+        UnsupportedMessageImpl(ImMsgBody.Elem())
     )
 
     private val emptySource = Mirai.constructMessageSource(