mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-12 22:10:14 +08:00
Voice serialization (#1368)
* Voice serialization * Implement `hashCode` and `equals` for `Voice` * Update test * Update test * Add note of `PttMessage.pttInternalInstance` * Fix logic of `Voice.equals`
This commit is contained in:
parent
1ad00134fa
commit
a4b62b0909
@ -5064,31 +5064,29 @@ public final class net/mamoe/mirai/message/data/VipFace$Kind$Companion {
|
|||||||
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||||
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||||
public static final field SERIAL_NAME Ljava/lang/String;
|
public static final field SERIAL_NAME Ljava/lang/String;
|
||||||
public synthetic fun <init> (ILjava/lang/String;[BJILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public fun contentToString ()Ljava/lang/String;
|
public fun contentToString ()Ljava/lang/String;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
public final fun getCodec ()I
|
public final fun getCodec ()I
|
||||||
public fun getFileName ()Ljava/lang/String;
|
public fun getFileName ()Ljava/lang/String;
|
||||||
public fun getFileSize ()J
|
public fun getFileSize ()J
|
||||||
public fun getMd5 ()[B
|
public fun getMd5 ()[B
|
||||||
public final fun getUrl ()Ljava/lang/String;
|
public final fun getUrl ()Ljava/lang/String;
|
||||||
|
public fun hashCode ()I
|
||||||
public fun toString ()Ljava/lang/String;
|
public fun toString ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||||
public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor;
|
}
|
||||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
|
||||||
|
public final class net/mamoe/mirai/message/data/Voice$Serializer : kotlinx/serialization/KSerializer {
|
||||||
|
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$Serializer;
|
||||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
||||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
|
||||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||||
|
@ -5064,31 +5064,29 @@ public final class net/mamoe/mirai/message/data/VipFace$Kind$Companion {
|
|||||||
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||||
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||||
public static final field SERIAL_NAME Ljava/lang/String;
|
public static final field SERIAL_NAME Ljava/lang/String;
|
||||||
public synthetic fun <init> (ILjava/lang/String;[BJILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
public fun contentToString ()Ljava/lang/String;
|
public fun contentToString ()Ljava/lang/String;
|
||||||
|
public fun equals (Ljava/lang/Object;)Z
|
||||||
public final fun getCodec ()I
|
public final fun getCodec ()I
|
||||||
public fun getFileName ()Ljava/lang/String;
|
public fun getFileName ()Ljava/lang/String;
|
||||||
public fun getFileSize ()J
|
public fun getFileSize ()J
|
||||||
public fun getMd5 ()[B
|
public fun getMd5 ()[B
|
||||||
public final fun getUrl ()Ljava/lang/String;
|
public final fun getUrl ()Ljava/lang/String;
|
||||||
|
public fun hashCode ()I
|
||||||
public fun toString ()Ljava/lang/String;
|
public fun toString ()Ljava/lang/String;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||||
public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor;
|
}
|
||||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
|
||||||
|
public final class net/mamoe/mirai/message/data/Voice$Serializer : kotlinx/serialization/KSerializer {
|
||||||
|
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$Serializer;
|
||||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
||||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
|
||||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||||
|
@ -15,8 +15,10 @@ import kotlinx.coroutines.Job
|
|||||||
import net.mamoe.kjbb.JvmBlockingBridge
|
import net.mamoe.kjbb.JvmBlockingBridge
|
||||||
import net.mamoe.mirai.contact.*
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.data.*
|
import net.mamoe.mirai.data.*
|
||||||
|
import net.mamoe.mirai.message.data.Voice
|
||||||
import net.mamoe.mirai.utils.ExternalResource
|
import net.mamoe.mirai.utils.ExternalResource
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||||
|
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||||
import net.mamoe.mirai.utils.WeakRef
|
import net.mamoe.mirai.utils.WeakRef
|
||||||
import kotlin.annotation.AnnotationTarget.*
|
import kotlin.annotation.AnnotationTarget.*
|
||||||
|
|
||||||
@ -293,4 +295,17 @@ public interface LowLevelApiAccessor {
|
|||||||
seconds: Int,
|
seconds: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化 [Voice.pttInternalInstance]
|
||||||
|
*/
|
||||||
|
@LowLevelApi
|
||||||
|
@MiraiInternalApi // For Voice serialize
|
||||||
|
public fun serializePttElem(ptt: Any?): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反序列化 [Voice.pttInternalInstance]
|
||||||
|
*/
|
||||||
|
@LowLevelApi
|
||||||
|
@MiraiInternalApi // For Voice serialize
|
||||||
|
public fun deserializePttElem(ptt: String): Any?
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.message.data
|
package net.mamoe.mirai.message.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
|
import net.mamoe.mirai.Mirai
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.Group
|
||||||
import net.mamoe.mirai.utils.ExternalResource
|
import net.mamoe.mirai.internal.message.map
|
||||||
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsVoice
|
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsVoice
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
|
||||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
|
||||||
import net.mamoe.mirai.utils.safeCast
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,9 +41,30 @@ public abstract class PttMessage : MessageContent {
|
|||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
public abstract val fileSize: Long
|
public abstract val fileSize: Long
|
||||||
|
|
||||||
|
/*
|
||||||
|
* **internal impl note**
|
||||||
|
* 用于中转 ImMsgBody.Ptt, 在接受到其他用户发送的语音时能按照原样发回,
|
||||||
|
* 并且便于未来修改 (对 api 修改最小化)
|
||||||
|
*/
|
||||||
@MiraiInternalApi
|
@MiraiInternalApi
|
||||||
@Transient
|
@Transient
|
||||||
public var pttInternalInstance: Any? = null
|
public var pttInternalInstance: Any? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
_pttInternalInstanceSerializeCache = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiInternalApi
|
||||||
|
protected val pttInternalInstanceSerializeCache: String
|
||||||
|
get() {
|
||||||
|
_pttInternalInstanceSerializeCache?.let { return it }
|
||||||
|
return Mirai.serializePttElem(pttInternalInstance).also {
|
||||||
|
_pttInternalInstanceSerializeCache = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private var _pttInternalInstanceSerializeCache: String? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +79,8 @@ public abstract class PttMessage : MessageContent {
|
|||||||
*
|
*
|
||||||
* [Voice] 实例可以通过序列化方式保存. 下次可以用它发送因而不需要上传. 但可能由于未来服务器更新, 这项功能就不稳定. 因此建议总是上传音频文件而不要保存 [Voice].
|
* [Voice] 实例可以通过序列化方式保存. 下次可以用它发送因而不需要上传. 但可能由于未来服务器更新, 这项功能就不稳定. 因此建议总是上传音频文件而不要保存 [Voice].
|
||||||
*/
|
*/
|
||||||
@Serializable // experimental
|
@Suppress("DuplicatedCode")
|
||||||
|
@Serializable(Voice.Serializer::class) // experimental
|
||||||
@SerialName(Voice.SERIAL_NAME)
|
@SerialName(Voice.SERIAL_NAME)
|
||||||
public class Voice @MiraiInternalApi constructor(
|
public class Voice @MiraiInternalApi constructor(
|
||||||
@MiraiExperimentalApi public override val fileName: String,
|
@MiraiExperimentalApi public override val fileName: String,
|
||||||
@ -92,4 +114,70 @@ public class Voice @MiraiInternalApi constructor(
|
|||||||
public override fun toString(): String = _stringValue!!
|
public override fun toString(): String = _stringValue!!
|
||||||
|
|
||||||
public override fun contentToString(): String = "[语音消息]"
|
public override fun contentToString(): String = "[语音消息]"
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is Voice) return false
|
||||||
|
|
||||||
|
if (this.pttInternalInstance != null && other.pttInternalInstance != null) {
|
||||||
|
if (this.pttInternalInstance == other.pttInternalInstance)
|
||||||
|
return true
|
||||||
|
// strict
|
||||||
|
return this.pttInternalInstanceSerializeCache == other.pttInternalInstanceSerializeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName != other.fileName) return false
|
||||||
|
if (!md5.contentEquals(other.md5)) return false
|
||||||
|
if (fileSize != other.fileSize) return false
|
||||||
|
if (codec != other.codec) return false
|
||||||
|
if (_url != other._url) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
if (pttInternalInstance != null)
|
||||||
|
return pttInternalInstanceSerializeCache.hashCode()
|
||||||
|
|
||||||
|
var result = fileName.hashCode()
|
||||||
|
result = 12 * result + md5.contentHashCode()
|
||||||
|
result = 54 * result + fileSize.hashCode()
|
||||||
|
result = 33 * result + codec
|
||||||
|
result = 15 * result + _url.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Serializer : KSerializer<Voice> by VoiceS.serializer().map(
|
||||||
|
resultantDescriptor = VoiceS.serializer().descriptor.copy(SERIAL_NAME),
|
||||||
|
deserialize = {
|
||||||
|
Voice(
|
||||||
|
fileName = it.fileName,
|
||||||
|
md5 = it.md5,
|
||||||
|
fileSize = it.fileSize,
|
||||||
|
codec = it.codec,
|
||||||
|
_url = it._url,
|
||||||
|
).also { v -> v.pttInternalInstance = Mirai.deserializePttElem(it.ptt) }
|
||||||
|
},
|
||||||
|
serialize = {
|
||||||
|
VoiceS(
|
||||||
|
fileName = it.fileName,
|
||||||
|
md5 = it.md5,
|
||||||
|
fileSize = it.fileSize,
|
||||||
|
_url = it._url,
|
||||||
|
codec = it.codec,
|
||||||
|
ptt = Mirai.serializePttElem(it.pttInternalInstance)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
@SerialName(SERIAL_NAME)
|
||||||
|
private class VoiceS(
|
||||||
|
val fileName: String,
|
||||||
|
val md5: ByteArray,
|
||||||
|
val fileSize: Long,
|
||||||
|
val codec: Int,
|
||||||
|
val _url: String,
|
||||||
|
val ptt: String = "",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1182,4 +1182,14 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun serializePttElem(ptt: Any?): String {
|
||||||
|
if (ptt !is ImMsgBody.Ptt) return ""
|
||||||
|
return ptt.toByteArray(ImMsgBody.Ptt.serializer()).toUHexString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializePttElem(ptt: String): Any? {
|
||||||
|
if (ptt.isBlank()) return null
|
||||||
|
return ptt.hexToBytes().loadAs(ImMsgBody.Ptt.serializer())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,4 +217,42 @@ internal class MessageSerializationTest {
|
|||||||
actual = source
|
actual = source
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class V(
|
||||||
|
val msg: Voice
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test Voice serialization`() {
|
||||||
|
val v = V(Voice("4517", byteArrayOf(14), 50, 3, "https://github.com"))
|
||||||
|
println(v.serialize(V.serializer()))
|
||||||
|
assertEquals(
|
||||||
|
v.serialize(V.serializer()),
|
||||||
|
v.serialize(V.serializer())
|
||||||
|
.deserialize(V.serializer())
|
||||||
|
.serialize(V.serializer())
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
v,
|
||||||
|
v.serialize(V.serializer()).deserialize(V.serializer())
|
||||||
|
)
|
||||||
|
v.msg.pttInternalInstance = ImMsgBody.Ptt(
|
||||||
|
srcUin = 1234567890,
|
||||||
|
fileMd5 = byteArrayOf(14, 81, 37, 14),
|
||||||
|
boolValid = true,
|
||||||
|
format = 90,
|
||||||
|
)
|
||||||
|
println(v.serialize(V.serializer()))
|
||||||
|
assertEquals(
|
||||||
|
v.serialize(V.serializer()),
|
||||||
|
v.serialize(V.serializer())
|
||||||
|
.deserialize(V.serializer())
|
||||||
|
.serialize(V.serializer())
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
v,
|
||||||
|
v.serialize(V.serializer()).deserialize(V.serializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user