- Add FileMessage.internalId hence support serialization

- Add FileMessage.Serializer
- Add FileMessage.create and IMirai.createFileMessage to construct manually
- Mark FileMessage as stable
- Move TestMiraiCode from mirai-core-api to mirai-core

fix #1082
This commit is contained in:
Him188 2021-03-20 15:33:08 +08:00
parent 5521bb2eec
commit af58c163d2
13 changed files with 167 additions and 50 deletions

View File

@ -88,6 +88,7 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public fun calculateGroupCodeByGroupUin (J)J
public fun calculateGroupUinByGroupCode (J)J
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 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;
@ -4046,11 +4047,13 @@ public final class net/mamoe/mirai/message/data/Face$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public static final field Key Lnet/mamoe/mirai/message/data/FileMessage$Key;
public static final field SERIAL_NAME Ljava/lang/String;
public fun contentToString ()Ljava/lang/String;
public static fun create (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
public abstract fun getId ()Ljava/lang/String;
public abstract fun getInternalId ()I
public fun getKey ()Lnet/mamoe/mirai/message/data/FileMessage$Key;
public synthetic fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey;
public abstract fun getName ()Ljava/lang/String;
@ -4061,6 +4064,16 @@ public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/m
public final class net/mamoe/mirai/message/data/FileMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
public static final field SERIAL_NAME Ljava/lang/String;
public final fun create (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
}
public final class net/mamoe/mirai/message/data/FileMessage$Serializer : kotlinx/serialization/KSerializer {
public static final field INSTANCE Lnet/mamoe/mirai/message/data/FileMessage$Serializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/FileMessage;
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/FileMessage;)V
}
public final class net/mamoe/mirai/message/data/FlashImage : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/HummerMessage, net/mamoe/mirai/message/data/MessageContent {
@ -4689,6 +4702,7 @@ public final class net/mamoe/mirai/message/data/MessageSourceKind$Companion {
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 at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
public static final synthetic fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;

View File

@ -88,6 +88,7 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
public fun calculateGroupCodeByGroupUin (J)J
public fun calculateGroupUinByGroupCode (J)J
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 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;
@ -4046,11 +4047,13 @@ public final class net/mamoe/mirai/message/data/Face$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent {
public static final field Key Lnet/mamoe/mirai/message/data/FileMessage$Key;
public static final field SERIAL_NAME Ljava/lang/String;
public fun contentToString ()Ljava/lang/String;
public static fun create (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
public abstract fun getId ()Ljava/lang/String;
public abstract fun getInternalId ()I
public fun getKey ()Lnet/mamoe/mirai/message/data/FileMessage$Key;
public synthetic fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey;
public abstract fun getName ()Ljava/lang/String;
@ -4061,6 +4064,16 @@ public abstract interface class net/mamoe/mirai/message/data/FileMessage : net/m
public final class net/mamoe/mirai/message/data/FileMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
public static final field SERIAL_NAME Ljava/lang/String;
public final fun create (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
}
public final class net/mamoe/mirai/message/data/FileMessage$Serializer : kotlinx/serialization/KSerializer {
public static final field INSTANCE Lnet/mamoe/mirai/message/data/FileMessage$Serializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/FileMessage;
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/FileMessage;)V
}
public final class net/mamoe/mirai/message/data/FlashImage : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/HummerMessage, net/mamoe/mirai/message/data/MessageContent {
@ -4689,6 +4702,7 @@ public final class net/mamoe/mirai/message/data/MessageSourceKind$Companion {
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 at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
public static final synthetic fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;

View File

@ -137,6 +137,12 @@ public interface IMirai : LowLevelApiAccessor {
*/
public fun createImage(imageId: String): Image
/**
* 创建一个 [FileMessage]. [name] [size] 只供本地使用, 发送消息时只会使用 [id] [internalId].
* @since 2.5
*/
public fun createFileMessage(id: String, internalId: Int, name: String, size: Long): FileMessage
/**
* 获取图片下载链接
*

View File

@ -27,6 +27,9 @@ import kotlin.reflect.KClass
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.isSubclassOf
internal fun SerialDescriptor.copy(newName: String): SerialDescriptor =
buildClassSerialDescriptor(newName) { takeElementsFrom(this@copy) }
internal fun ClassSerialDescriptorBuilder.takeElementsFrom(descriptor: SerialDescriptor) {
with(descriptor) {

View File

@ -10,7 +10,7 @@
package net.mamoe.mirai.message.code
import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.MiraiExperimentalApi
@ -19,14 +19,6 @@ import net.mamoe.mirai.utils.MiraiExperimentalApi
*
* 从字符串解析 mirai [MiraiCode.deserializeMiraiCode]
*
* @see At
* @see AtAll
* @see VipFace
* @see Face
* @see Image
* @see FlashImage
* @see PokeMessage
*
* @see MiraiCode
*/
public interface CodableMessage : Message {

View File

@ -130,6 +130,9 @@ private object MiraiCodeParsers: AbstractMap<String, MiraiCodeParser>(), Map<Str
MusicShare(MusicKind.valueOf(kind), title, summary, jumpUrl, pictureUrl, musicUrl, brief)
},
"file" to MiraiCodeParser(Regex("""(.*?),(.*?),(.*?),(.*?)""")) { (id, internalId, name, size) ->
FileMessage(id, internalId.toInt(), name, size.toLong())
},
)

View File

@ -7,27 +7,61 @@
* 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.kjbb.JvmBlockingBridge
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.FileSupported
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.internal.message.copy
import net.mamoe.mirai.internal.message.map
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.RemoteFile
import net.mamoe.mirai.utils.safeCast
/**
* 文件消息.
*
* [name] [size] 只供本地使用, 发送消息时只会使用 [id] [internalId].
*
* ### 文件操作
* 要下载这个文件, 可通过 [toRemoteFile] 获取到 [RemoteFile] 然后操作.
*
* 要获取到 [FileMessage], 可以通过 [MessageEvent.message] 获取, 或通过 [RemoteFile.upload] 上传.
*
* @since 2.5
* @suppress 文件消息不稳定, 可能在未来版本有不兼容变更.
* @suppress [FileMessage] 的使用是稳定的, 但自行实现不稳定.
*/
@Serializable(FileMessage.Serializer::class)
@SerialName(FileMessage.SERIAL_NAME)
@MiraiExperimentalApi
public interface FileMessage : MessageContent, ConstrainSingle {
public val name: String
public interface FileMessage : MessageContent, ConstrainSingle, CodableMessage {
/**
* 服务器需要的某种 ID.
*/
public val id: String
/**
* 服务器需要的某种 ID.
*/
public val internalId: Int
/**
* 文件名
*/
public val name: String
/**
* 文件大小 bytes
*/
public val size: Long
override fun contentToString(): String = "[文件]$name" // orthodox
@ -35,7 +69,6 @@ public interface FileMessage : MessageContent, ConstrainSingle {
/**
* 获取一个对应的 [RemoteFile]. 当目标群或好友不存在这个文件时返回 `null`.
*/
@MiraiExperimentalApi
@JvmBlockingBridge
public suspend fun toRemoteFile(contact: FileSupported): RemoteFile? {
return contact.filesRoot.resolveById(id)
@ -43,8 +76,47 @@ public interface FileMessage : MessageContent, ConstrainSingle {
override val key: Key get() = Key
/**
* 注意, baseKey [MessageContent] 不稳定. 未来可能会有变更.
*/
public companion object Key :
AbstractPolymorphicMessageKey<MessageContent, ForwardMessage>(MessageContent, { it.safeCast() }) {
AbstractPolymorphicMessageKey<@MiraiExperimentalApi MessageContent, ForwardMessage>(
MessageContent, { it.safeCast() }) {
public const val SERIAL_NAME: String = "FileMessage"
/**
* 构造 [FileMessage]
* @since 2.5
*/
@JvmStatic
public fun create(id: String, internalId: Int, name: String, size: Long): FileMessage =
Mirai.createFileMessage(id, internalId, name, size)
}
}
public object Serializer : KSerializer<FileMessage> by FallbackSerializer("FileMessage") // not polymorphic
@MiraiInternalApi
private open class FallbackSerializer(serialName: String) : KSerializer<FileMessage> by Delegate.serializer().map(
Delegate.serializer().descriptor.copy(serialName),
serialize = { Delegate(id, internalId, name, size) },
deserialize = { Mirai.createFileMessage(id, internalId, name, size) },
) {
@SerialName(Image.SERIAL_NAME)
@Serializable
data class Delegate(
val id: String,
val internalId: Int,
val name: String,
val size: Long,
)
}
}
/**
* 构造 [FileMessage]
* @since 2.5
*/
@JvmSynthetic
public inline fun FileMessage(id: String, internalId: Int, name: String, size: Long): FileMessage =
FileMessage.create(id, internalId, name, size)

View File

@ -23,7 +23,6 @@ import kotlinx.serialization.json.*
import net.mamoe.mirai.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
@ -900,6 +899,10 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}
}
override fun createFileMessage(id: String, internalId: Int, name: String, size: Long): FileMessage {
return FileMessageImpl(id, internalId, name, size)
}
@Suppress("DEPRECATION", "OverridingDeprecatedMember")
override suspend fun queryImageUrl(bot: Bot, image: Image): String = when (image) {
is ConstOriginUrlAware -> image.originUrl

View File

@ -22,34 +22,22 @@ internal fun FileMessage.checkIsImpl(): FileMessageImpl {
@Serializable
@SerialName(FileMessage.SERIAL_NAME)
internal class FileMessageImpl(
override val name: String,
internal data class FileMessageImpl(
override val id: String,
@SerialName("internalId") val busId: Int,
override val name: String,
override val size: Long,
val busId: Int // internal // TODO: 2021/3/8 introduce OnlineFileMessage and OfflineFileMessage to eliminate property `busId`.
) : FileMessage {
override fun toString(): String = "[mirai:file:$name,$id]"
override val internalId: Int
get() = busId
@Suppress("DuplicatedCode")
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FileMessageImpl
if (name != other.name) return false
if (id != other.id) return false
if (size != other.size) return false
return true
}
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + id.hashCode()
result = 31 * result + size.hashCode()
result = 31 * result + busId.hashCode()
return result
override fun appendMiraiCodeTo(builder: StringBuilder) {
builder.append("[mirai:file:")
builder.append(id).append(",")
builder.append(busId).append(",")
builder.append(name).append(",")
builder.append(size).append("]")
}
override fun toString(): String = "[mirai:file:$name,$id,$size,$busId]"
}

View File

@ -376,10 +376,10 @@ private object ReceiveMessageTransformer {
list.add(
FileMessageImpl(
id = file.filePath,
busId = file.busId, // path i.e. /a99e95fa-7b2d-11eb-adae-5452007b698a
name = file.fileName,
id = file.filePath, // path i.e. /a99e95fa-7b2d-11eb-adae-5452007b698a
size = file.fileSize,
busId = file.busId
size = file.fileSize
)
)
}

View File

@ -507,7 +507,7 @@ internal class RemoteFileImpl(
): FileMessage {
val resp = upload0(resource, null) ?: error("Failed to upload file.")
return FileMessageImpl(
name, resp.fileId, resource.size, resp.busId
resp.fileId, resp.busId, name, resource.size
)
}
@ -544,6 +544,6 @@ internal class RemoteFileImpl(
override suspend fun toMessage(): FileMessage? {
val info = getFileFolderInfo() ?: return null
if (!info.isFile) return null
return FileMessageImpl(name, info.id, info.size, info.busId)
return FileMessageImpl(info.id, info.busId, name, info.size)
}
}

View File

@ -7,7 +7,9 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.message.code
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") // MiraiCodeParser
package net.mamoe.mirai.internal.message.code
import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode
import net.mamoe.mirai.message.code.internal.MiraiCodeParser
@ -32,7 +34,12 @@ class TestMiraiCode {
}
@Test
fun testCodes() {
fun `test serialization`() {
assertEquals("[mirai:file:id,1,name,2]", FileMessage("id", 1, "name", 2).serializeToMiraiCode())
}
@Test
fun `test deserialization`() {
assertEquals(AtAll.toMessageChain(), "[mirai:atall]".deserializeMiraiCode())
assertEquals(PlainText("[Hello").toMessageChain(), "\\[Hello".deserializeMiraiCode())
assertEquals(buildMessageChain {
@ -62,6 +69,7 @@ class TestMiraiCode {
assertEquals(buildMessageChain {
+Dice(1)
}, "[mirai:dice:1]".deserializeMiraiCode())
assertEquals(FileMessage("id", 1, "name", 2), "[mirai:file:id,1,name,2]".deserializeMiraiCode().single())
val musicShare = MusicShare(
kind = MusicKind.NeteaseCloudMusic,

View File

@ -17,6 +17,7 @@ import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
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.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.message.MessageSerializers
@ -111,6 +112,7 @@ internal class MessageSerializationTest {
RichMessageOrigin(SimpleServiceMessage(1, "content"), "resource id", RichMessageKind.LONG),
ShowImageFlag,
Dice(1),
FileMessageImpl("id", 2, "name", 1)
)
companion object {
@ -121,6 +123,18 @@ internal class MessageSerializationTest {
}
}
@Test
fun `test FileMessage serialization`() {
@Serializable
data class W(
val m: FileMessage
)
val w = W(FileMessageImpl("id", 2, "name", 1))
println(w.serialize(W.serializer()))
assertEquals(w, w.serialize(W.serializer()).deserialize(W.serializer()))
}
@Test
fun `test polymorphic serialization`() {
@Serializable