mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-12 14:00:12 +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 static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||
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 fun contentToString ()Ljava/lang/String;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCodec ()I
|
||||
public fun getFileName ()Ljava/lang/String;
|
||||
public fun getFileSize ()J
|
||||
public fun getMd5 ()[B
|
||||
public final fun getUrl ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
||||
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$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun serializer ()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 fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
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/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 {
|
||||
|
@ -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 static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||
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 fun contentToString ()Ljava/lang/String;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCodec ()I
|
||||
public fun getFileName ()Ljava/lang/String;
|
||||
public fun getFileSize ()J
|
||||
public fun getMd5 ()[B
|
||||
public final fun getUrl ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
||||
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$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun serializer ()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 fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
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/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 {
|
||||
|
@ -15,8 +15,10 @@ import kotlinx.coroutines.Job
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.WeakRef
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
|
||||
@ -293,4 +295,17 @@ public interface LowLevelApiAccessor {
|
||||
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
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.Mirai
|
||||
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.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
|
||||
/**
|
||||
@ -41,9 +41,30 @@ public abstract class PttMessage : MessageContent {
|
||||
@MiraiExperimentalApi
|
||||
public abstract val fileSize: Long
|
||||
|
||||
/*
|
||||
* **internal impl note**
|
||||
* 用于中转 ImMsgBody.Ptt, 在接受到其他用户发送的语音时能按照原样发回,
|
||||
* 并且便于未来修改 (对 api 修改最小化)
|
||||
*/
|
||||
@MiraiInternalApi
|
||||
@Transient
|
||||
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].
|
||||
*/
|
||||
@Serializable // experimental
|
||||
@Suppress("DuplicatedCode")
|
||||
@Serializable(Voice.Serializer::class) // experimental
|
||||
@SerialName(Voice.SERIAL_NAME)
|
||||
public class Voice @MiraiInternalApi constructor(
|
||||
@MiraiExperimentalApi public override val fileName: String,
|
||||
@ -92,4 +114,70 @@ public class Voice @MiraiInternalApi constructor(
|
||||
public override fun toString(): String = _stringValue!!
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
@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