Message serialization, fix #767

This commit is contained in:
Him188 2021-01-06 13:13:39 +08:00
parent bfd21cbb92
commit ebc7d655e3
30 changed files with 452 additions and 394 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -10,15 +10,12 @@
package net.mamoe.mirai.message
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.PolymorphicModuleBuilder
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.plus
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.*
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.MiraiExperimentalApi
@ -29,21 +26,44 @@ import kotlin.reflect.KClass
public interface MessageSerializer {
public val serializersModule: SerializersModule
public fun <M : Message> registerSerializer(clazz: KClass<M>, serializer: KSerializer<M>)
public fun <M : SingleMessage> registerSerializer(baseClass: KClass<M>, serializer: KSerializer<M>)
public fun registerSerializers(serializersModule: SerializersModule)
public fun clearRegisteredSerializers()
public companion object INSTANCE : MessageSerializer by MessageSerializerImpl
}
internal fun ClassSerialDescriptorBuilder.takeElementsFrom(descriptor: SerialDescriptor) {
with(descriptor) {
repeat(descriptor.elementsCount) { index ->
element(
elementName = getElementName(index),
descriptor = getElementDescriptor(index),
annotations = getElementAnnotations(index),
isOptional = isElementOptional(index),
)
}
}
}
@MiraiInternalApi
public open class MessageSourceSerializerImpl(serialName: String) : KSerializer<MessageSource> {
public companion object : MessageSourceSerializerImpl("net.mamoe.mirai.message.data.MessageSource")
public open class MessageSourceSerializerImpl(serialName: String) :
KSerializer<MessageSource> by SerialData.serializer().map(
resultantDescriptor = buildClassSerialDescriptor(serialName) {
takeElementsFrom(SerialData.serializer().descriptor)
},
serialize = { SerialData(kind, botId, ids, internalIds, time, fromId, targetId, originalMessage) },
deserialize = {
Mirai.constructMessageSource(botId, kind, fromId, targetId, ids, time, internalIds, originalMessage)
}
) {
@SerialName(MessageSource.SERIAL_NAME)
@Serializable
internal class SerialData(
val kind: MessageSourceKind,
val bot: Long,
val botId: Long,
val ids: IntArray,
val internalIds: IntArray,
val time: Int,
@ -51,49 +71,6 @@ public open class MessageSourceSerializerImpl(serialName: String) : KSerializer<
val targetId: Long,
val originalMessage: MessageChain,
)
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(serialName) {
val desc = SerialData.serializer().descriptor
repeat(SerialData.serializer().descriptor.elementsCount) { index ->
element(
desc.getElementName(index),
desc.getElementDescriptor(index),
desc.getElementAnnotations(index),
desc.isElementOptional(index)
)
}
}
// buildClassSerialDescriptor("MessageSource") {
// element("bot", Long.serializer().descriptor)
// element("ids", ArraySerializer(Int.serializer()).descriptor)
// element("internalIds", ArraySerializer(Int.serializer()).descriptor)
// element("time", Int.serializer().descriptor)
// element("fromId", Int.serializer().descriptor)
// element("targetId", Int.serializer().descriptor)
// element("originalMessage", MessageChain.Serializer.descriptor)
// }
override fun deserialize(decoder: Decoder): MessageSource {
val data = SerialData.serializer().deserialize(decoder)
data.run {
return Mirai.constructMessageSource(
botId = bot, kind = kind, fromUin = fromId, targetUin = targetId, ids = ids,
time = time, internalIds = internalIds, originalMessage = originalMessage.asMessageChain()
)
}
}
override fun serialize(encoder: Encoder, value: MessageSource) {
value.run {
SerialData.serializer().serialize(
encoder = encoder,
value = SerialData(
kind = kind, bot = botId, ids = ids, internalIds = internalIds,
time = time, fromId = fromId, targetId = targetId, originalMessage = originalMessage
)
)
}
}
}
@ -121,8 +98,6 @@ private val builtInSerializersModule by lazy {
contextual(LightApp::class, LightApp.serializer())
contextual(SimpleServiceMessage::class, SimpleServiceMessage.serializer())
contextual(AbstractServiceMessage::class, AbstractServiceMessage.serializer())
contextual(LongMessage::class, LongMessage.serializer())
contextual(ForwardMessageInternal::class, ForwardMessageInternal.serializer())
contextual(PttMessage::class, PttMessage.serializer())
contextual(Voice::class, Voice.serializer())
@ -130,12 +105,7 @@ private val builtInSerializersModule by lazy {
contextual(VipFace::class, VipFace.serializer())
contextual(FlashImage::class, FlashImage.serializer())
fun PolymorphicModuleBuilder<SingleMessage>.singleMessageSubclasses() {
// subclass(MessageSource::class, MessageSource.serializer())
}
// contextual(MessageSource::class, MessageSource.serializer())
polymorphicDefault(MessageSource::class) { MessageSource.serializer() }
contextual(MessageSource::class, MessageSource.serializer())
fun PolymorphicModuleBuilder<MessageMetadata>.messageMetadataSubclasses() {
subclass(MessageSource::class, MessageSource.serializer())
@ -154,8 +124,6 @@ private val builtInSerializersModule by lazy {
subclass(LightApp::class, LightApp.serializer())
subclass(SimpleServiceMessage::class, SimpleServiceMessage.serializer())
subclass(LongMessage::class, LongMessage.serializer())
subclass(ForwardMessageInternal::class, ForwardMessageInternal.serializer())
// subclass(PttMessage::class, PttMessage.serializer())
subclass(Voice::class, Voice.serializer())
@ -166,21 +134,11 @@ private val builtInSerializersModule by lazy {
subclass(FlashImage::class, FlashImage.serializer())
}
@Suppress("DEPRECATION_ERROR")
contextual(Message::class, Message.Serializer)
// contextual(SingleMessage::class, SingleMessage.Serializer)
contextual(SingleMessage::class, SingleMessage.Serializer)
contextual(MessageChain::class, MessageChain.Serializer)
contextual(MessageChainImpl::class, MessageChainImpl.serializer())
polymorphic(MessageChain::class) {
subclass(MessageChainImpl::class, MessageChainImpl.serializer())
}
polymorphicDefault(MessageChain::class) { MessageChainImpl.serializer() }
polymorphic(AbstractServiceMessage::class) {
subclass(LongMessage::class, LongMessage.serializer())
subclass(ForwardMessageInternal::class, ForwardMessageInternal.serializer())
}
// polymorphicDefault(MessageChain::class) { MessageChainImpl.serializer() }
// polymorphic(SingleMessage::class) {
// subclass(MessageSource::class, MessageSource.serializer())
@ -189,16 +147,12 @@ private val builtInSerializersModule by lazy {
// }
// }
polymorphicDefault(Image::class) { Image.Serializer }
// polymorphic(Message::class) {
// subclass(PlainText::class, PlainText.serializer())
// }
polymorphic(Message::class) {
polymorphic(SingleMessage::class) {
messageContentSubclasses()
messageMetadataSubclasses()
singleMessageSubclasses()
subclass(MessageChainImpl::class, MessageChainImpl.serializer())
}
//contextual(SingleMessage::class, SingleMessage.Serializer)
@ -216,7 +170,7 @@ private val builtInSerializersModule by lazy {
// contextual(MessageMetadata::class, MessageMetadata.Serializer)
// polymorphic(MessageMetadata::class, MessageMetadata.Serializer) {
// messageMetadataSubclasses()
// }
//
}
}
@ -226,22 +180,51 @@ internal object MessageSerializerImpl : MessageSerializer {
override val serializersModule: SerializersModule get() = serializersModuleField ?: builtInSerializersModule
@Synchronized
override fun <M : Message> registerSerializer(clazz: KClass<M>, serializer: KSerializer<M>) {
serializersModuleField = serializersModule.plus(SerializersModule {
contextual(clazz, serializer)
polymorphic(Message::class) {
subclass(clazz, serializer)
override fun <M : SingleMessage> registerSerializer(baseClass: KClass<M>, serializer: KSerializer<M>) {
serializersModuleField = serializersModule.overwriteWith(SerializersModule {
contextual(baseClass, serializer)
polymorphic(SingleMessage::class) {
subclass(baseClass, serializer)
}
})
}
@Synchronized
override fun registerSerializers(serializersModule: SerializersModule) {
serializersModuleField = serializersModule
serializersModuleField = serializersModule.overwriteWith(serializersModule)
}
@Synchronized
override fun clearRegisteredSerializers() {
serializersModuleField = builtInSerializersModule
}
}
internal inline fun <T, R> KSerializer<T>.map(
resultantDescriptor: SerialDescriptor,
crossinline deserialize: T.(T) -> R,
crossinline serialize: R.(R) -> T,
): KSerializer<R> {
return object : KSerializer<R> {
override val descriptor: SerialDescriptor get() = resultantDescriptor
override fun deserialize(decoder: Decoder): R = this@map.deserialize(decoder).let { deserialize(it, it) }
override fun serialize(encoder: Encoder, value: R) = serialize(encoder, value.let { serialize(it, it) })
}
}
internal inline fun <T, R> KSerializer<T>.mapPrimitive(
serialName: String,
crossinline deserialize: (T) -> R,
crossinline serialize: R.(R) -> T,
): KSerializer<R> {
val kind = this@mapPrimitive.descriptor.kind
check(kind is PrimitiveKind) { "kind must be PrimitiveKind but found $kind" }
return object : KSerializer<R> {
override fun deserialize(decoder: Decoder): R =
this@mapPrimitive.deserialize(decoder).let(deserialize)
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(serialName, kind)
override fun serialize(encoder: Encoder, value: R) =
this@mapPrimitive.serialize(encoder, value.let { serialize(it, it) })
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -14,6 +14,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
@ -30,6 +31,7 @@ import net.mamoe.mirai.message.code.CodableMessage
*
* @see AtAll 全体成员
*/
@SerialName(At.SERIAL_NAME)
@Serializable
public data class At(
public val target: Long,
@ -51,7 +53,9 @@ public data class At(
builder.append("[mirai:at:").append(target).append(']')
}
public companion object;
public companion object {
public const val SERIAL_NAME: String = "At"
}
// 自动为消息补充 " "
public override fun followedBy(tail: Message): MessageChain {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,6 +13,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
@ -28,10 +29,12 @@ private const val displayA = "@全体成员"
*
* @see At at 单个群成员
*/
@SerialName(AtAll.SERIAL_NAME)
@Serializable
public object AtAll :
MessageContent, CodableMessage {
public const val display: String = displayA
public const val SERIAL_NAME: String = "AtAll"
@Suppress("SpellCheckingInspection")
public override fun toString(): String = "[mirai:atall]"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -12,6 +12,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
@ -22,10 +23,11 @@ import net.mamoe.mirai.message.code.CodableMessage
* 格式: &#91;mirai:face:*[id]*&#93;
*/
@Serializable
@SerialName(Face.SERIAL_NAME)
public data class Face(public val id: Int) : // used in delegation
MessageContent, CodableMessage {
public override fun toString(): String = "[mirai:face:$id]";
public override fun toString(): String = "[mirai:face:$id]"
public val name: String get() = contentToString().let { it.substring(1, it.length - 1) }
public override fun contentToString(): String = names.getOrElse(id) { "[表情]" }
@ -39,6 +41,9 @@ public data class Face(public val id: Int) : // used in delegation
//Auto generated
@Suppress("NonAsciiCharacters", "unused", "SpellCheckingInspection", "all")
public companion object {
public const val SERIAL_NAME: String = "Face"
public const val JING_YA: Int = 0
public const val 惊讶: Int = JING_YA
public const val PIE_ZUI: Int = 1

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,6 +13,7 @@ package net.mamoe.mirai.message.data
import io.ktor.http.*
import io.ktor.util.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
@ -96,6 +97,7 @@ public data class RawForwardMessage(
*
* @see buildForwardMessage
*/
@SerialName(ForwardMessage.SERIAL_NAME)
@Serializable
public data class ForwardMessage(
val preview: List<String>,
@ -136,7 +138,7 @@ public data class ForwardMessage(
* Java 用户: 使用 [sequenceOf] (`SequenceKt.sequenceOf`) [asSequence] (`SequenceKt.asSequence`)
*/
public fun generatePreview(forward: RawForwardMessage): List<String> =
forward.nodeList.map { it.senderName + ": " + it.message.contentToString() }
forward.nodeList.map { it.senderName + ": " + it.messageChain.contentToString() }
/**
* 显示在卡片底部
@ -152,8 +154,15 @@ public data class ForwardMessage(
override val senderId: Long,
override val time: Int,
override val senderName: String,
override val message: Message
) : INode
override val messageChain: MessageChain
) : INode {
public constructor(
senderId: Long,
time: Int,
senderName: String,
message: Message
) : this(senderId, time, senderName, message.asMessageChain())
}
@MiraiExperimentalApi
public interface INode {
@ -175,11 +184,13 @@ public data class ForwardMessage(
/**
* 消息内容
*/
public val message: Message
public val messageChain: MessageChain
}
public companion object Key :
AbstractPolymorphicMessageKey<MessageContent, ForwardMessage>(MessageContent, { it.safeCast() })
AbstractPolymorphicMessageKey<MessageContent, ForwardMessage>(MessageContent, { it.safeCast() }) {
public const val SERIAL_NAME: String = "ForwardMessage"
}
}
@ -358,7 +369,7 @@ public class ForwardMessageBuilder private constructor(
/**
* 消息内容
*/
public override lateinit var message: Message
public override lateinit var messageChain: MessageChain
/**
@ -410,19 +421,21 @@ public class ForwardMessageBuilder private constructor(
* 指定消息内容
*/
@ForwardMessageDsl
public infix fun message(message: Message): BuilderNode = this.apply { this.message = message }
public infix fun message(message: Message): BuilderNode =
this.apply { this.messageChain = message.asMessageChain() }
/**
* 指定消息内容
*/
@ForwardMessageDsl
public infix fun message(message: String): BuilderNode = this.apply { this.message = PlainText(message) }
public infix fun message(message: String): BuilderNode =
this.apply { this.messageChain = PlainText(message).asMessageChain() }
/** 添加一条消息 */
@ForwardMessageDsl
public infix fun says(message: Message): ForwardMessageBuilder = this@ForwardMessageBuilder.apply {
checkBuilt()
this@BuilderNode.message = message
this@BuilderNode.messageChain = message.asMessageChain()
add(this@BuilderNode)
}
@ -454,7 +467,7 @@ public class ForwardMessageBuilder private constructor(
checkBuilt()
add(BuilderNode().apply {
senderId = this@says
this.message = message
this.messageChain = message.asMessageChain()
})
}
@ -551,7 +564,7 @@ public class ForwardMessageBuilder private constructor(
it.senderId,
it.time,
it.senderName,
it.message
it.messageChain
)
}).render(this.displayStrategy)
@ -565,5 +578,5 @@ public class ForwardMessageBuilder private constructor(
internal inline fun Int.toLongUnsigned(): Long = this.toLong().and(0xFFFF_FFFF)
private fun ForwardMessage.INode.toNode(): ForwardMessage.Node {
return ForwardMessage.Node(senderId, time, senderName, message)
return ForwardMessage.Node(senderId, time, senderName, messageChain)
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,7 +13,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.code.internal.appendAsMiraiCode
@ -46,6 +46,7 @@ public interface HummerMessage : MessageContent, ConstrainSingle {
*
* @see PokeMessage.Companion 使用伴生对象中的常量
*/
@SerialName(PokeMessage.SERIAL_NAME)
@Serializable
public data class PokeMessage @MiraiInternalApi constructor(
/**
@ -63,6 +64,8 @@ public data class PokeMessage @MiraiInternalApi constructor(
public companion object Key :
AbstractPolymorphicMessageKey<HummerMessage, PokeMessage>(HummerMessage, { it.castOrNull() }) {
public const val SERIAL_NAME: String = "PokeMessage"
/** 戳一戳 */
@JvmField
public val ChuoYiChuo: PokeMessage = PokeMessage("戳一戳", 1, -1)
@ -195,7 +198,9 @@ public interface MarketFace : HummerMessage {
get() = Key
public companion object Key :
AbstractPolymorphicMessageKey<HummerMessage, MarketFace>(HummerMessage, { it.safeCast() })
AbstractPolymorphicMessageKey<HummerMessage, MarketFace>(HummerMessage, { it.safeCast() }) {
public const val SERIAL_NAME: String = "MarketFace"
}
override fun contentToString(): String = name
}
@ -215,6 +220,7 @@ public interface MarketFace : HummerMessage {
*
* @see VipFace.Key 使用伴生对象中的常量
*/
@SerialName(VipFace.SERIAL_NAME)
@Serializable
public data class VipFace @MiraiInternalApi constructor(
/**
@ -239,6 +245,8 @@ public data class VipFace @MiraiInternalApi constructor(
public companion object Key :
AbstractPolymorphicMessageKey<HummerMessage, VipFace>(HummerMessage, { it.safeCast() }) {
public const val SERIAL_NAME: String = "VipFace"
@JvmField
public val LiuLian: Kind = 9 to "榴莲"
@ -310,12 +318,14 @@ public data class VipFace @MiraiInternalApi constructor(
*
* @see Image 查看图片相关信息
*/
@SerialName(FlashImage.SERIAL_NAME)
@Serializable
public data class FlashImage(
/**
* 闪照的内容图片, 即一个普通图片.
*/
@Contextual
@SerialName("imageId")
@Serializable(Image.AsStringSerializer::class)
public val image: Image
) : MessageContent, HummerMessage, CodableMessage, ConstrainSingle {
override val key: MessageKey<FlashImage>
@ -323,6 +333,7 @@ public data class FlashImage(
public companion object Key :
AbstractPolymorphicMessageKey<HummerMessage, FlashImage>(HummerMessage, { it.safeCast() }) {
public const val SERIAL_NAME: String = "FlashImage"
/**
* 将普通图片转换为闪照.

View File

@ -20,14 +20,10 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.encoding.encodeStructure
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.Bot
import net.mamoe.mirai.IMirai
@ -40,6 +36,8 @@ import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2
import net.mamoe.mirai.message.data.Image.Key.queryUrl
import net.mamoe.mirai.message.map
import net.mamoe.mirai.message.mapPrimitive
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage
@ -90,26 +88,32 @@ public interface Image : Message, MessageContent, CodableMessage {
*/
public val imageId: String
public object Serializer : KSerializer<Image> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("net.mamoe.mirai.message.data.Image") {
element("imageId", String.serializer().descriptor)
}
@kotlinx.serialization.Serializer(forClass = Image::class)
public object AsStringSerializer : KSerializer<Image> by String.serializer().mapPrimitive(
SERIAL_NAME,
serialize = { imageId },
deserialize = { Image(it) },
)
override fun deserialize(decoder: Decoder): Image = decoder.decodeStructure(descriptor) {
val imageId = if (decodeSequentially()) {
decodeStringElement(descriptor, 0)
} else {
decodeStringElement(descriptor, decodeElementIndex(descriptor))
}
Image(imageId)
}
@kotlinx.serialization.Serializer(forClass = Image::class)
public object Serializer : KSerializer<Image> by FallbackSerializer("Image")
override fun serialize(encoder: Encoder, value: Image): Unit = encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.imageId)
}
@MiraiInternalApi
public open class FallbackSerializer(serialName: String) : KSerializer<Image> by Delegate.serializer().map(
buildClassSerialDescriptor(serialName) { element("imageId", String.serializer().descriptor) },
serialize = { Delegate(imageId) },
deserialize = { Image(imageId) },
) {
@SerialName(SERIAL_NAME)
@Serializable
internal data class Delegate(
val imageId: String
)
}
public companion object Key : AbstractMessageKey<Image>({ it.safeCast() }) {
public const val SERIAL_NAME: String = "Image"
/**
* 通过 [Image.imageId] 构造一个 [Image] 以便发送.
* 这个图片必须是服务器已经存在的图片.
@ -148,8 +152,9 @@ public interface Image : Message, MessageContent, CodableMessage {
@Suppress("RegExpRedundantEscape") // This is required on Android
@JvmStatic
@get:JvmName("getImageIdRegex")
public val IMAGE_ID_REGEX: Regex =
Regex("""\{[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\}\..{3,5}""")
// inline because compilation error
public inline val IMAGE_ID_REGEX: Regex
get() = Regex("""\{[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\}\..{3,5}""")
/**
* 图片资源 ID 正则表达式 1. mirai 内部使用.
@ -160,7 +165,9 @@ public interface Image : Message, MessageContent, CodableMessage {
@JvmStatic
@MiraiInternalApi
@get:JvmName("getImageResourceIdRegex1")
public val IMAGE_RESOURCE_ID_REGEX_1: Regex = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""")
// inline because compilation error
public inline val IMAGE_RESOURCE_ID_REGEX_1: Regex
get() = Regex("""/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}""")
/**
* 图片资源 ID 正则表达式 2. mirai 内部使用.
@ -171,7 +178,9 @@ public interface Image : Message, MessageContent, CodableMessage {
@JvmStatic
@MiraiInternalApi
@get:JvmName("getImageResourceIdRegex2")
public val IMAGE_RESOURCE_ID_REGEX_2: Regex = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""")
// inline because compilation error
public inline val IMAGE_RESOURCE_ID_REGEX_2: Regex
get() = Regex("""/[0-9]*-[0-9]*-[0-9a-fA-F]{32}""")
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -20,12 +20,9 @@ package net.mamoe.mirai.message.data
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.fold
import kotlinx.serialization.*
import kotlinx.serialization.json.Json
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.MessageSerializer
import net.mamoe.mirai.message.MessageSerializerImpl
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.safeCast
import kotlin.contracts.contract
@ -73,8 +70,6 @@ import kotlin.internal.LowPriorityInOverloadResolution
*
* @see Contact.sendMessage 发送消息
*/
@Suppress("DEPRECATION_ERROR")
@Serializable(Message.Serializer::class)
public interface Message { // must be interface. Don't consider any changes.
/**
@ -198,50 +193,7 @@ public interface Message { // must be interface. Don't consider any changes.
public operator fun plus(another: Sequence<Message>): MessageChain =
another.fold(this, Message::plus).asMessageChain()
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
public object Serializer :
MessageSerializer by MessageSerializerImpl,
KSerializer<Message> by PolymorphicSerializer(Message::class)
@Suppress("DEPRECATION_ERROR")
public companion object {
/**
* JSON 字符串解析 [Message]
* @see serializeToJsonString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@JvmOverloads
@JvmStatic
public fun deserializeFromJsonString(
string: String,
json: Json = Json { serializersModule = Serializer.serializersModule }
): Message {
return json.decodeFromString(Serializer, string)
}
/**
* [Message] 序列化为 JSON 字符串.
* @see deserializeFromJsonString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@JvmOverloads
@JvmStatic
public fun Message.serializeToJsonString(
json: Json = Json { serializersModule = Serializer.serializersModule }
): String = json.encodeToString(Serializer, this)
/**
* [Message] 序列化为指定格式的字符串.
*
* @see serializeToJsonString
* @see StringFormat.encodeToString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@ExperimentalSerializationApi
@JvmStatic
public fun Message.serializeToString(format: StringFormat): String =
format.encodeToString(Serializer, this)
}
public companion object
}
@ -320,10 +272,11 @@ public inline operator fun Message.times(count: Int): MessageChain = this.repeat
/**
* 单个消息元素. 与之相对的是 [MessageChain], 是多个 [SingleMessage] 的集合.
*/
// @Serializable(SingleMessage.Serializer::class)
@Serializable(SingleMessage.Serializer::class)
public interface SingleMessage : Message {
// @kotlinx.serialization.Serializer(forClass = SingleMessage::class)
// public object Serializer : KSerializer<SingleMessage> by PolymorphicSerializer(SingleMessage::class)
@kotlinx.serialization.Serializer(forClass = SingleMessage::class)
public object Serializer :
KSerializer<SingleMessage> by PolymorphicSerializer(SingleMessage::class)
}
/**
@ -339,14 +292,11 @@ public interface SingleMessage : Message {
*
* @see ConstrainSingle 约束一个 [MessageChain] 中只存在这一种类型的元素
*/
@Serializable(MessageMetadata.Serializer::class)
public interface MessageMetadata : SingleMessage {
/**
* 返回空字符串
*/
override fun contentToString(): String = ""
public object Serializer : KSerializer<MessageMetadata> by PolymorphicSerializer(MessageMetadata::class)
}
/**
@ -381,11 +331,8 @@ public interface ConstrainSingle : SingleMessage {
* @see ForwardMessage 合并转发
* @see Voice 语音
*/
@Serializable(MessageContent.Serializer::class)
public interface MessageContent : SingleMessage {
public companion object Key : AbstractMessageKey<MessageContent>({ it.safeCast() })
public object Serializer : KSerializer<MessageContent> by PolymorphicSerializer(MessageContent::class)
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -20,6 +20,7 @@ import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.message.MessageSerializerImpl
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.data.MessageSource.Key.quote
import net.mamoe.mirai.message.data.MessageSource.Key.recall
@ -92,9 +93,10 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
public operator fun <M : SingleMessage> contains(key: MessageKey<M>): Boolean =
asSequence().any { key.safeCast.invoke(it) != null }
@kotlinx.serialization.Serializer(MessageChain::class)
public object Serializer : KSerializer<MessageChain> {
@Suppress("DEPRECATION_ERROR")
private val delegate = ListSerializer<Message>(Message.Serializer)
private val delegate = ListSerializer(PolymorphicSerializer(SingleMessage::class))
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MessageChain = delegate.deserialize(decoder).asMessageChain()
override fun serialize(encoder: Encoder, value: MessageChain): Unit = delegate.serialize(encoder, value)
@ -110,12 +112,11 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
* JSON 字符串解析 [MessageChain]
* @see serializeToJsonString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@JvmOverloads
@JvmStatic
public fun deserializeFromJsonString(
string: String,
json: Json = Json { serializersModule = Message.Serializer.serializersModule }
json: Json = Json { serializersModule = MessageSerializerImpl.serializersModule }
): MessageChain {
return json.decodeFromString(Serializer, string)
}
@ -124,12 +125,11 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
* [MessageChain] 序列化为 JSON 字符串.
* @see deserializeFromJsonString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@JvmOverloads
@JvmStatic
public fun MessageChain.serializeToJsonString(
json: Json = Json { serializersModule = Message.Serializer.serializersModule }
): String = json.encodeToString(Message.Serializer, this)
json: Json = Json { serializersModule = MessageSerializerImpl.serializersModule }
): String = json.encodeToString(MessageChain.Serializer, this)
/**
* [MessageChain] 序列化为指定格式的字符串.
@ -137,7 +137,6 @@ public interface MessageChain : Message, List<SingleMessage>, RandomAccess, Coda
* @see serializeToJsonString
* @see StringFormat.encodeToString
*/
@Deprecated("消息序列化仍未稳定,请在 2.0-RC 再使用", level = DeprecationLevel.HIDDEN)
@ExperimentalSerializationApi
@JvmStatic
public fun MessageChain.serializeToString(format: StringFormat): String =

View File

@ -36,7 +36,6 @@ public interface MessageKey<out M : SingleMessage> {
* @see AbstractPolymorphicMessageKey
*/
public abstract class AbstractMessageKey<out M : SingleMessage>(
@JvmField
override val safeCast: (SingleMessage) -> M?,
) : MessageKey<M>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -68,7 +68,7 @@ import net.mamoe.mirai.utils.safeCast
*
* @see buildMessageSource 构造一个 [OfflineMessageSource]
*/
@Serializable(MessageSourceSerializerImpl.Companion::class)
@Serializable(MessageSource.Serializer::class)
public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
public final override val key: MessageKey<MessageSource>
get() = Key
@ -150,7 +150,11 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
public final override fun toString(): String =
"[mirai:source:${ids.contentToString()},${internalIds.contentToString()}]"
public object Serializer : MessageSourceSerializerImpl("MessageSource")
public companion object Key : AbstractMessageKey<MessageSource>({ it.safeCast() }) {
public const val SERIAL_NAME: String = "MessageSource"
/**
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
*
@ -361,7 +365,8 @@ public sealed class OnlineMessageSource : MessageSource() {
}
public abstract class ToStranger : Outgoing() {
public companion object Key : AbstractPolymorphicMessageKey<Outgoing, ToStranger>(Outgoing, { it.safeCast() })
public companion object Key :
AbstractPolymorphicMessageKey<Outgoing, ToStranger>(Outgoing, { it.safeCast() })
public abstract override val target: Stranger
public final override val subject: Stranger get() = target

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,6 +13,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.code.internal.appendAsMiraiCode
@ -23,6 +24,7 @@ import net.mamoe.mirai.message.code.internal.appendAsMiraiCode
* 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [Message.plus]
*/
@Serializable
@SerialName(PlainText.SERIAL_NAME)
public data class PlainText(
public val content: String
) : MessageContent, CodableMessage {
@ -36,7 +38,9 @@ public data class PlainText(
builder.appendAsMiraiCode(content)
}
public companion object
public companion object {
public const val SERIAL_NAME: String = "PlainText"
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,6 +13,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import net.mamoe.mirai.message.data.MessageSource.Key.recall
@ -40,8 +41,13 @@ import net.mamoe.mirai.utils.safeCast
* @see MessageSource 获取有关消息源的更多信息
*/
@Serializable
public data class QuoteReply(public val source: MessageSource) : Message, MessageMetadata, ConstrainSingle {
public companion object Key : AbstractMessageKey<QuoteReply>({ it.safeCast() })
@SerialName(QuoteReply.SERIAL_NAME)
public data class QuoteReply(
public val source: MessageSource
) : Message, MessageMetadata, ConstrainSingle {
public companion object Key : AbstractMessageKey<QuoteReply>({ it.safeCast() }) {
public const val SERIAL_NAME: String = "QuoteReply"
}
public override val key: MessageKey<QuoteReply> get() = Key

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -13,6 +13,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.message.code.CodableMessage
import net.mamoe.mirai.message.code.internal.appendAsMiraiCode
@ -95,8 +96,11 @@ public interface RichMessage : MessageContent, ConstrainSingle {
* @see ServiceMessage 服务消息
*/
@Serializable
@SerialName(LightApp.SERIAL_NAME)
public data class LightApp(override val content: String) : RichMessage, CodableMessage {
public companion object Key : AbstractMessageKey<LightApp>({ it.safeCast() })
public companion object Key : AbstractMessageKey<LightApp>({ it.safeCast() }) {
public const val SERIAL_NAME: String = "LightApp"
}
public override fun toString(): String = "[mirai:app:$content]"
@ -117,6 +121,7 @@ public data class LightApp(override val content: String) : RichMessage, CodableM
*/
@MiraiExperimentalApi
@Serializable
@SerialName(SimpleServiceMessage.SERIAL_NAME)
public class SimpleServiceMessage(
public override val serviceId: Int,
public override val content: String
@ -136,6 +141,9 @@ public class SimpleServiceMessage(
return result
}
public companion object {
public const val SERIAL_NAME: String = "SimpleServiceMessage"
}
}
@ -257,8 +265,7 @@ public class XmlMessageBuilder(
}
}
@Serializable
@MiraiExperimentalApi
// internal runtime value, not serializable
internal data class LongMessage internal constructor(override val content: String, val resId: String) :
AbstractServiceMessage() {
override val serviceId: Int get() = 35
@ -266,7 +273,7 @@ internal data class LongMessage internal constructor(override val content: Strin
companion object Key : AbstractPolymorphicMessageKey<ServiceMessage, LongMessage>(ServiceMessage, { it.safeCast() })
}
@Serializable
// internal runtime value, not serializable
internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage() {
override val serviceId: Int get() = 35
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -9,6 +9,7 @@
package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.MiraiInternalApi
@ -40,6 +41,7 @@ public abstract class PttMessage : MessageContent {
* 语音消息, 目前只支持接收和转发
*/
@Serializable // experimental
@SerialName(Voice.SERIAL_NAME)
public class Voice @MiraiInternalApi constructor(
@MiraiExperimentalApi public override val fileName: String,
@MiraiExperimentalApi public override val md5: ByteArray,
@ -49,7 +51,9 @@ public class Voice @MiraiInternalApi constructor(
private val _url: String
) : PttMessage() {
public companion object Key : AbstractPolymorphicMessageKey<PttMessage, Voice>(PttMessage, { it.safeCast() })
public companion object Key : AbstractPolymorphicMessageKey<PttMessage, Voice>(PttMessage, { it.safeCast() }) {
public const val SERIAL_NAME: String = "Voice"
}
public val url: String?
get() = when {

View File

@ -156,7 +156,7 @@ internal fun <M : SingleMessage> MessageChain.getImpl(key: MessageKey<M>): M? {
/**
* 使用 [Collection] 作为委托的 [MessageChain]
*/
@Serializable
@Serializable(MessageChain.Serializer::class)
internal data class MessageChainImpl constructor(
@JvmField
internal val delegate: List<SingleMessage> // 必须 constrainSingleMessages, 且为 immutable

View File

@ -34,6 +34,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.MessageSerializer
import net.mamoe.mirai.message.action.Nudge
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX
@ -50,17 +51,54 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
companion object INSTANCE : MiraiImpl() {
@Suppress("ObjectPropertyName", "unused", "DEPRECATION_ERROR")
private val _init = Mirai.let {
Message.Serializer.registerSerializer(OfflineGroupImage::class, OfflineGroupImage.serializer())
Message.Serializer.registerSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer())
Message.Serializer.registerSerializer(MarketFaceImpl::class, MarketFaceImpl.serializer())
Message.Serializer.registerSerializer(
MessageSerializer.registerSerializer(OfflineGroupImage::class, OfflineGroupImage.serializer())
MessageSerializer.registerSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer())
MessageSerializer.registerSerializer(OnlineFriendImageImpl::class, OnlineFriendImageImpl.serializer())
MessageSerializer.registerSerializer(OnlineGroupImageImpl::class, OnlineGroupImageImpl.serializer())
MessageSerializer.registerSerializer(MarketFaceImpl::class, MarketFaceImpl.serializer())
// MessageSource
MessageSerializer.registerSerializer(
OnlineMessageSourceFromGroupImpl::class,
OnlineMessageSourceFromGroupImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceFromFriendImpl::class,
OnlineMessageSourceFromFriendImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceFromTempImpl::class,
OnlineMessageSourceFromTempImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceFromStrangerImpl::class,
OnlineMessageSourceFromStrangerImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceToGroupImpl::class,
OnlineMessageSourceToGroupImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceToFriendImpl::class,
OnlineMessageSourceToFriendImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceToTempImpl::class,
OnlineMessageSourceToTempImpl.serializer()
)
MessageSerializer.registerSerializer(
OnlineMessageSourceToStrangerImpl::class,
OnlineMessageSourceToStrangerImpl.serializer()
)
MessageSerializer.registerSerializer(
OfflineMessageSourceImplData::class,
OfflineMessageSourceImplData.serializer()
)
Message.Serializer.registerSerializer(
MessageSourceFromGroupImpl::class,
MessageSourceFromGroupImpl.serializer()
MessageSerializer.registerSerializer(
OfflineMessageSourceImplData::class,
OfflineMessageSourceImplData.serializer()
)
}
}
@ -305,12 +343,12 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}
val response: PbMessageSvc.PbMsgWithDraw.Response = when (source) {
is MessageSourceToGroupImpl,
is MessageSourceFromGroupImpl
is OnlineMessageSourceToGroupImpl,
is OnlineMessageSourceFromGroupImpl
-> {
val group = when (source) {
is MessageSourceToGroupImpl -> source.target
is MessageSourceFromGroupImpl -> source.group
is OnlineMessageSourceToGroupImpl -> source.target
is OnlineMessageSourceFromGroupImpl -> source.group
else -> error("stub")
}
if (bot.id != source.fromId) {
@ -326,10 +364,10 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
}
}
is MessageSourceFromFriendImpl,
is MessageSourceToFriendImpl,
is MessageSourceFromStrangerImpl,
is MessageSourceToStrangerImpl,
is OnlineMessageSourceFromFriendImpl,
is OnlineMessageSourceToFriendImpl,
is OnlineMessageSourceFromStrangerImpl,
is OnlineMessageSourceToStrangerImpl,
-> network.run {
check(source.fromId == bot.id) {
"can only recall a message sent by bot"
@ -342,13 +380,13 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
source.time
).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
}
is MessageSourceFromTempImpl,
is MessageSourceToTempImpl
is OnlineMessageSourceFromTempImpl,
is OnlineMessageSourceToTempImpl
-> network.run {
check(source.fromId == bot.id) {
"can only recall a message sent by bot"
}
source as MessageSourceToTempImpl
source as OnlineMessageSourceToTempImpl
PbMessageSvc.PbMsgWithDraw.createForTempMessage(
bot.client,
(source.target.group as GroupImpl).uin,
@ -592,7 +630,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
forwardMessage: ForwardMessage?
): MessageReceipt<Group> = with(bot.asQQAndroidBot()) {
message.forEach {
it.message.ensureSequenceIdAvailable()
it.messageChain.ensureSequenceIdAvailable()
}
val group = getGroupOrFail(groupCode)
@ -655,7 +693,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
if (isLong) {
group.sendMessage(
RichMessage.longMessage(
brief = message.joinToString(limit = 27) { it.message.contentToString() },
brief = message.joinToString(limit = 27) { it.messageChain.contentToString() },
resId = resId,
timeSeconds = time
)

View File

@ -170,7 +170,7 @@ internal class GroupImpl(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds().toInt(),
message = chain,
messageChain = chain,
senderName = bot.nick
)
),
@ -195,7 +195,7 @@ internal class GroupImpl(
}
val result = bot.network.runCatching {
val source: MessageSourceToGroupImpl
val source: OnlineMessageSourceToGroupImpl
MessageSvcPbSendMsg.createToGroup(
bot.client,
this@GroupImpl,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -19,7 +19,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.message.MessageSourceToTempImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceToTempImpl
import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
import net.mamoe.mirai.internal.message.firstIsInstanceOrNull
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
@ -81,7 +81,7 @@ internal class NormalMemberImpl constructor(
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
val result = bot.network.runCatching {
val source: MessageSourceToTempImpl
val source: OnlineMessageSourceToTempImpl
MessageSvcPbSendMsg.createToTemp(
bot.client,
this@NormalMemberImpl,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -16,8 +16,8 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.asQQAndroidBot
import net.mamoe.mirai.internal.message.MessageSourceToFriendImpl
import net.mamoe.mirai.internal.message.MessageSourceToStrangerImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceToFriendImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend
@ -37,8 +37,8 @@ internal inline val Bot.uin: Long get() = this.id
internal suspend fun <T : User> Friend.sendMessageImpl(
message: Message,
friendReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt<Friend>,
tReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt<T>
friendReceiptConstructor: (OnlineMessageSourceToFriendImpl) -> MessageReceipt<Friend>,
tReceiptConstructor: (OnlineMessageSourceToFriendImpl) -> MessageReceipt<T>
): MessageReceipt<T> {
contract { callsInPlace(friendReceiptConstructor, InvocationKind.EXACTLY_ONCE) }
val bot = bot.asQQAndroidBot()
@ -56,7 +56,7 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
lateinit var source: MessageSourceToFriendImpl
lateinit var source: OnlineMessageSourceToFriendImpl
val result = bot.network.runCatching {
MessageSvcPbSendMsg.createToFriend(
bot.client,
@ -89,8 +89,8 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
internal suspend fun <T : User> Stranger.sendMessageImpl(
message: Message,
strangerReceiptConstructor: (MessageSourceToStrangerImpl) -> MessageReceipt<Stranger>,
tReceiptConstructor: (MessageSourceToStrangerImpl) -> MessageReceipt<T>
strangerReceiptConstructor: (OnlineMessageSourceToStrangerImpl) -> MessageReceipt<Stranger>,
tReceiptConstructor: (OnlineMessageSourceToStrangerImpl) -> MessageReceipt<T>
): MessageReceipt<T> {
contract { callsInPlace(strangerReceiptConstructor, InvocationKind.EXACTLY_ONCE) }
val bot = bot.asQQAndroidBot()
@ -108,7 +108,7 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
lateinit var source: MessageSourceToStrangerImpl
lateinit var source: OnlineMessageSourceToStrangerImpl
val result = bot.network.runCatching {
MessageSvcPbSendMsg.createToStranger(
bot.client,

View File

@ -328,13 +328,13 @@ internal fun List<MsgComm.Msg>.toMessageChain(
checkNotNull(bot) { "bot is null" }
when (messageSourceKind) {
MessageSourceKind.TEMP -> +MessageSourceFromTempImpl(bot, this@toMessageChain)
MessageSourceKind.GROUP -> +MessageSourceFromGroupImpl(bot, this@toMessageChain)
MessageSourceKind.FRIEND -> +MessageSourceFromFriendImpl(bot, this@toMessageChain)
MessageSourceKind.STRANGER -> +MessageSourceFromStrangerImpl(bot, this@toMessageChain)
MessageSourceKind.TEMP -> +OnlineMessageSourceFromTempImpl(bot, this@toMessageChain)
MessageSourceKind.GROUP -> +OnlineMessageSourceFromGroupImpl(bot, this@toMessageChain)
MessageSourceKind.FRIEND -> +OnlineMessageSourceFromFriendImpl(bot, this@toMessageChain)
MessageSourceKind.STRANGER -> +OnlineMessageSourceFromStrangerImpl(bot, this@toMessageChain)
}
} else {
+OfflineMessageSourceImplByMsg(bot, this@toMessageChain, botId)
+OfflineMessageSourceImplData(bot, this@toMessageChain, botId)
}
elements.joinToMessageChain(groupIdOrZero, botId, this)
addAll(ptts)
@ -348,7 +348,7 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(botId: Long, groupIdOrZero: Long
if (elements.isEmpty())
error("elements for SourceMsg is empty")
return buildMessageChain(elements.size + 1) {
+OfflineMessageSourceImplBySourceMsg(
+OfflineMessageSourceImplData(
delegate = this@toMessageChain,
botId = botId,
groupIdOrZero = groupIdOrZero
@ -422,7 +422,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, botId:
this.forEach { element ->
when {
element.srcMsg != null -> {
list.add(QuoteReply(OfflineMessageSourceImplBySourceMsg(element.srcMsg, botId, groupIdOrZero)))
list.add(QuoteReply(OfflineMessageSourceImplData(element.srcMsg, botId, groupIdOrZero)))
}
element.notOnlineImage != null -> list.add(OnlineFriendImageImpl(element.notOnlineImage))
element.customFace != null -> list.add(OnlineGroupImageImpl(element.customFace))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -10,6 +10,7 @@
package net.mamoe.mirai.internal.message
import kotlinx.io.core.toByteArray
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
@ -43,6 +44,7 @@ internal fun Face.toCommData(): ImMsgBody.CommonElem {
}
@SerialName(MarketFace.SERIAL_NAME)
@Serializable
internal data class MarketFaceImpl internal constructor(
internal val delegate: ImMsgBody.MarketFace,

View File

@ -30,10 +30,13 @@ import net.mamoe.mirai.utils.generateImageIdFromResourceId
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
@Serializable(with = OnlineGroupImageImpl.Serializer::class)
internal class OnlineGroupImageImpl(
internal val delegate: ImMsgBody.CustomFace
) : @Suppress("DEPRECATION")
OnlineGroupImage() {
) : OnlineGroupImage() {
object Serializer : Image.FallbackSerializer("OnlineGroupImage")
override val imageId: String = generateImageId(
delegate.picMd5,
delegate.filePath.substringAfterLast('.')
@ -58,10 +61,13 @@ OnlineGroupImage() {
}
}
@Serializable(with = OnlineFriendImageImpl.Serializer::class)
internal class OnlineFriendImageImpl(
internal val delegate: ImMsgBody.NotOnlineImage
) : @Suppress("DEPRECATION")
OnlineFriendImage() {
object Serializer : Image.FallbackSerializer("OnlineFriendImage")
override val imageId: String =
generateImageIdFromResourceId(delegate.resId, getImageType(delegate.imgType)) ?: delegate.resId
override val originUrl: String
@ -212,7 +218,6 @@ internal interface SuspendDeferredOriginUrlAware : Image {
suspend fun getUrl(bot: Bot): String
}
@Suppress("EXPOSED_SUPER_INTERFACE")
internal interface OnlineImage : Image, ConstOriginUrlAware {
override val originUrl: String
}
@ -228,11 +233,12 @@ internal interface OfflineImage : Image
/**
* @param imageId 参考 [Image.imageId]
*/
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@Serializable
@Serializable(with = OfflineGroupImage.Serializer::class)
internal data class OfflineGroupImage(
override val imageId: String
) : GroupImage(), OfflineImage, DeferredOriginUrlAware {
object Serializer : Image.FallbackSerializer("OfflineGroupImage")
override fun getUrl(bot: Bot): String {
return "http://gchat.qpic.cn/gchatpic_new/${bot.id}/0-0-${
imageId.substring(1..36)
@ -265,10 +271,12 @@ internal val Image.friendImageId: String
*
* @param imageId 参考 [Image.imageId]
*/
@Serializable
@Serializable(with = OfflineFriendImage.Serializer::class)
internal data class OfflineFriendImage(
override val imageId: String
) : FriendImage(), OfflineImage, DeferredOriginUrlAware {
object Serializer : Image.FallbackSerializer("OfflineFriendImage")
override fun getUrl(bot: Bot): String {
return "http://c2cpicdw.qpic.cn/offpic_new/${bot.id}${this.friendImageId}/0?term=2"
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -71,10 +71,13 @@ internal suspend inline fun Message.ensureSequenceIdAvailable() {
}*/
}
internal class MessageSourceFromFriendImpl(
@Serializable(OnlineMessageSourceFromFriendImpl.Serializer::class)
internal class OnlineMessageSourceFromFriendImpl(
override val bot: Bot,
val msg: List<MsgComm.Msg>
) : OnlineMessageSource.Incoming.FromFriend(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromFriend")
override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq }
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
override val ids: IntArray get() = sequenceIds// msg.msgBody.richText.attr!!.random
@ -99,10 +102,13 @@ internal class MessageSourceFromFriendImpl(
override fun toJceData(): ImMsgBody.SourceMsg = jceData
}
internal class MessageSourceFromStrangerImpl(
@Serializable(OnlineMessageSourceFromStrangerImpl.Serializer::class)
internal class OnlineMessageSourceFromStrangerImpl(
override val bot: Bot,
val msg: List<MsgComm.Msg>
) : OnlineMessageSource.Incoming.FromStranger(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromStranger")
override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq }
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
override val ids: IntArray get() = sequenceIds// msg.msgBody.richText.attr!!.random
@ -163,10 +169,13 @@ private fun List<MsgComm.Msg>.toJceDataPrivate(ids: IntArray): ImMsgBody.SourceM
)
}
internal class MessageSourceFromTempImpl(
@Serializable(OnlineMessageSourceFromTempImpl.Serializer::class)
internal class OnlineMessageSourceFromTempImpl(
override val bot: Bot,
private val msg: List<MsgComm.Msg>
) : OnlineMessageSource.Incoming.FromTemp(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromTemp")
override val sequenceIds: IntArray get() = msg.mapToIntArray { it.msgHead.msgSeq }
override val internalIds: IntArray get() = msg.mapToIntArray { it.msgBody.richText.attr!!.random }
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
@ -190,12 +199,12 @@ internal class MessageSourceFromTempImpl(
override fun toJceData(): ImMsgBody.SourceMsg = jceData
}
@Serializable(MessageSourceFromGroupImpl.Serializer::class)
internal data class MessageSourceFromGroupImpl(
@Serializable(OnlineMessageSourceFromGroupImpl.Serializer::class)
internal data class OnlineMessageSourceFromGroupImpl(
override val bot: Bot,
private val msg: List<MsgComm.Msg>
) : OnlineMessageSource.Incoming.FromGroup(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("net.mamoe.mirai.internal.message.MessageSourceFromGroupImpl")
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromGroupImpl")
@Transient
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -17,15 +17,15 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.message.MessageSourceSerializerImpl
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSourceKind
import net.mamoe.mirai.message.data.OfflineMessageSource
import net.mamoe.mirai.utils.mapToIntArray
import java.util.concurrent.atomic.AtomicBoolean
@Serializable
@Serializable(OfflineMessageSourceImplData.Serializer::class)
internal data class OfflineMessageSourceImplData(
override val kind: MessageSourceKind,
override val ids: IntArray,
@ -36,19 +36,30 @@ internal data class OfflineMessageSourceImplData(
override val originalMessage: MessageChain,
override val internalIds: IntArray,
) : OfflineMessageSource(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OfflineMessageSource")
override val sequenceIds: IntArray get() = ids
// for override.
// if provided, no need to serialize from message
@Transient
var originElems: List<ImMsgBody.Elem>? = null
// may provided by OfflineMessageSourceImplBySourceMsg
@Transient
var jceData: ImMsgBody.SourceMsg? = null
@Transient
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
override fun toJceData(): ImMsgBody.SourceMsg {
return ImMsgBody.SourceMsg(
return jceData ?: ImMsgBody.SourceMsg(
origSeqs = sequenceIds,
senderUin = fromId,
toUin = 0,
flag = 1,
elems = originalMessage.toRichTextElems(
elems = originElems ?: originalMessage.toRichTextElems(
null, //forGroup = kind == MessageSourceKind.GROUP,
withGeneralFlags = false
),
@ -56,7 +67,7 @@ internal data class OfflineMessageSourceImplData(
time = time,
pbReserve = net.mamoe.mirai.internal.EMPTY_BYTE_ARRAY,
srcMsg = net.mamoe.mirai.internal.EMPTY_BYTE_ARRAY
)
).also { jceData = it }
}
override fun equals(other: Any?): Boolean {
@ -90,92 +101,60 @@ internal data class OfflineMessageSourceImplData(
}
}
internal class OfflineMessageSourceImplByMsg(
// from other sources' originalMessage
internal fun OfflineMessageSourceImplData(
bot: Bot?,
val delegate: List<MsgComm.Msg>,
override val botId: Long,
) : OfflineMessageSource(), MessageSourceInternal {
override val kind: MessageSourceKind =
when {
delegate.first().msgHead.groupInfo != null -> {
MessageSourceKind.GROUP
}
delegate.first().msgHead.c2cTmpMsgHead != null -> {
MessageSourceKind.TEMP
}
bot?.getStranger(delegate.first().msgHead.fromUin) != null -> {
MessageSourceKind.STRANGER
}
else -> {
MessageSourceKind.FRIEND
}
delegate: List<MsgComm.Msg>,
botId: Long,
): OfflineMessageSourceImplData {
val kind = when {
delegate.first().msgHead.groupInfo != null -> {
MessageSourceKind.GROUP
}
override val ids: IntArray get() = sequenceIds
override val internalIds: IntArray = delegate.mapToIntArray { it.msgHead.msgUid.toInt() }
override val time: Int
get() = delegate.first().msgHead.msgTime
override val fromId: Long
get() = delegate.first().msgHead.fromUin
override val targetId: Long
get() = delegate.first().msgHead.groupInfo?.groupCode ?: delegate.first().msgHead.toUin
override val originalMessage: MessageChain by lazy {
delegate.toMessageChain(
delegate.first().msgHead.c2cTmpMsgHead != null -> {
MessageSourceKind.TEMP
}
bot?.getStranger(delegate.first().msgHead.fromUin) != null -> {
MessageSourceKind.STRANGER
}
else -> {
MessageSourceKind.FRIEND
}
}
return OfflineMessageSourceImplData(
kind = kind,
time = delegate.first().msgHead.msgTime,
fromId = delegate.first().msgHead.fromUin,
targetId = delegate.first().msgHead.groupInfo?.groupCode ?: delegate.first().msgHead.toUin,
originalMessage = delegate.toMessageChain(
null,
botId,
groupIdOrZero = delegate.first().msgHead.groupInfo?.groupCode ?: 0,
onlineSource = false,
messageSourceKind = kind
)
}
override val sequenceIds: IntArray = delegate.mapToIntArray { it.msgHead.msgSeq }
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
override fun toJceData(): ImMsgBody.SourceMsg {
return ImMsgBody.SourceMsg(
origSeqs = delegate.mapToIntArray { it.msgHead.msgSeq },
senderUin = delegate.first().msgHead.fromUin,
toUin = 0,
flag = 1,
elems = delegate.flatMap { it.msgBody.richText.elems },
type = 0,
time = delegate.first().msgHead.msgTime,
pbReserve = EMPTY_BYTE_ARRAY,
srcMsg = EMPTY_BYTE_ARRAY
)
),
ids = delegate.mapToIntArray { it.msgHead.msgSeq },
internalIds = delegate.mapToIntArray { it.msgHead.msgUid.toInt() },
botId = botId
).apply {
originElems = delegate.flatMap { it.msgBody.richText.elems }
}
}
internal class OfflineMessageSourceImplBySourceMsg(
// from others' quotation
val delegate: ImMsgBody.SourceMsg,
override val botId: Long,
groupIdOrZero: Long
) : OfflineMessageSource(), MessageSourceInternal {
override val kind: MessageSourceKind get() = if (delegate.srcMsg == null) MessageSourceKind.GROUP else MessageSourceKind.FRIEND
override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
override val sequenceIds: IntArray = delegate.origSeqs
override val internalIds: IntArray = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer())
.origUids?.mapToIntArray { it.toInt() } ?: intArrayOf()
override val time: Int get() = delegate.time
override val originalMessage: MessageChain by lazy { delegate.toMessageChain(botId, groupIdOrZero) }
/*
override val ids: Long
get() = (delegate.origSeqs?.firstOrNull()
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF)
*/
override val ids: IntArray get() = sequenceIds
// delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids?.toInt()
// ?: 0
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
override val fromId: Long get() = delegate.senderUin
override val targetId: Long by lazy {
when {
internal fun OfflineMessageSourceImplData(
delegate: ImMsgBody.SourceMsg,
botId: Long,
groupIdOrZero: Long,
): OfflineMessageSourceImplData {
return OfflineMessageSourceImplData(
kind = if (delegate.srcMsg == null) MessageSourceKind.GROUP else MessageSourceKind.FRIEND,
ids = delegate.origSeqs,
internalIds = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer())
.origUids?.mapToIntArray { it.toInt() } ?: intArrayOf(),
time = delegate.time,
originalMessage = delegate.toMessageChain(botId, groupIdOrZero),
fromId = delegate.senderUin,
targetId = when {
groupIdOrZero != 0L -> groupIdOrZero
delegate.toUin != 0L -> delegate.toUin
delegate.srcMsg != null -> delegate.srcMsg.loadAs(MsgComm.Msg.serializer()).msgHead.toUin
@ -187,10 +166,9 @@ internal class OfflineMessageSourceImplBySourceMsg(
)
}"
)*/
}
}
override fun toJceData(): ImMsgBody.SourceMsg {
return delegate
},
botId = botId
).apply {
jceData = delegate
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.message
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
@ -24,6 +25,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushPbPushGroupMsg.SendGroupMessageReceipt
import net.mamoe.mirai.internal.network.protocol.packet.chat.toLongUnsigned
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageSourceSerializerImpl
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
@ -72,7 +74,8 @@ private fun <T> T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg
)
}
internal class MessageSourceToFriendImpl(
@Serializable(OnlineMessageSourceToFriendImpl.Serializer::class)
internal class OnlineMessageSourceToFriendImpl(
override val sequenceIds: IntArray,
override val internalIds: IntArray,
override val time: Int,
@ -80,6 +83,8 @@ internal class MessageSourceToFriendImpl(
override val sender: Bot,
override val target: Friend
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
override val bot: Bot
get() = sender
override val ids: IntArray
@ -89,7 +94,8 @@ internal class MessageSourceToFriendImpl(
override fun toJceData(): ImMsgBody.SourceMsg = jceData
}
internal class MessageSourceToStrangerImpl(
@Serializable(OnlineMessageSourceToStrangerImpl.Serializer::class)
internal class OnlineMessageSourceToStrangerImpl(
override val sequenceIds: IntArray,
override val internalIds: IntArray,
override val time: Int,
@ -97,6 +103,8 @@ internal class MessageSourceToStrangerImpl(
override val sender: Bot,
override val target: Stranger
) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
override val bot: Bot
get() = sender
override val ids: IntArray
@ -106,7 +114,8 @@ internal class MessageSourceToStrangerImpl(
override fun toJceData(): ImMsgBody.SourceMsg = jceData
}
internal class MessageSourceToTempImpl(
@Serializable(OnlineMessageSourceToTempImpl.Serializer::class)
internal class OnlineMessageSourceToTempImpl(
override val sequenceIds: IntArray,
override val internalIds: IntArray,
override val time: Int,
@ -114,6 +123,8 @@ internal class MessageSourceToTempImpl(
override val sender: Bot,
override val target: Member
) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
override val bot: Bot
get() = sender
override val ids: IntArray
@ -123,7 +134,8 @@ internal class MessageSourceToTempImpl(
override fun toJceData(): ImMsgBody.SourceMsg = jceData
}
internal class MessageSourceToGroupImpl(
@Serializable(OnlineMessageSourceToGroupImpl.Serializer::class)
internal class OnlineMessageSourceToGroupImpl(
coroutineScope: CoroutineScope,
override val internalIds: IntArray,
override val time: Int,
@ -131,6 +143,8 @@ internal class MessageSourceToGroupImpl(
override val sender: Bot,
override val target: Group
) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal {
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
override val ids: IntArray
get() = sequenceIds
override val bot: Bot
@ -141,7 +155,7 @@ internal class MessageSourceToGroupImpl(
coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, Int>(
timeoutMillis = 3000
) {
if (it.messageRandom in this@MessageSourceToGroupImpl.internalIds) {
if (it.messageRandom in this@OnlineMessageSourceToGroupImpl.internalIds) {
it.sequenceId
} else null
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -71,7 +71,7 @@ internal fun Collection<ForwardMessage.INode>.calculateValidationDataForGroup(
),
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = chain.message.asMessageChain()
elems = chain.messageChain.asMessageChain()
.toRichTextElems(targetGroup, withGeneralFlags = false).toMutableList()
)
)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -17,10 +17,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
import kotlinx.io.core.readUShort
import kotlinx.io.core.*
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Group
@ -34,7 +30,7 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.message.MessageSourceFromFriendImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
import net.mamoe.mirai.internal.message.toMessageChain
import net.mamoe.mirai.internal.network.MultiPacket
import net.mamoe.mirai.internal.network.Packet
@ -437,7 +433,7 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot): Packet? {
?: return null// don't compare with dstAppId. diff.
val chain = buildMessageChain {
+MessageSourceFromFriendImpl(bot, listOf(this@transform))
+OnlineMessageSourceFromFriendImpl(bot, listOf(this@transform))
for (msgItem in textMsg.msgItems) {
when (msgItem.type) {
1 -> +PlainText(msgItem.text)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -143,7 +143,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient,
target: Stranger,
message: MessageChain,
source: MessageSourceToStrangerImpl
source: OnlineMessageSourceToStrangerImpl
): OutgoingPacket = buildOutgoingUniPacket(client) {
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
@ -173,7 +173,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient,
targetFriend: Friend,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
): List<OutgoingPacket> {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
@ -209,7 +209,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
},
postInit = {
sourceCallback(
MessageSourceToFriendImpl(
OnlineMessageSourceToFriendImpl(
internalIds = randIds.get(),
sender = client.bot,
target = targetFriend,
@ -262,7 +262,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient,
targetMember: Member,
message: MessageChain,
source: MessageSourceToTempImpl
source: OnlineMessageSourceToTempImpl
): OutgoingPacket = buildOutgoingUniPacket(client) {
writeProtoBuf(
MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq(
@ -292,7 +292,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
targetGroup: Group,
message: MessageChain,
isForward: Boolean,
source: MessageSourceToGroupImpl
source: OnlineMessageSourceToGroupImpl
): OutgoingPacket = buildOutgoingUniPacket(client) {
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
@ -354,12 +354,12 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
client: QQAndroidClient,
member: Member,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToTempImpl) -> Unit
crossinline sourceCallback: (OnlineMessageSourceToTempImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val source = MessageSourceToTempImpl(
val source = OnlineMessageSourceToTempImpl(
internalIds = intArrayOf(Random.nextInt().absoluteValue),
sender = client.bot,
target = member,
@ -380,12 +380,12 @@ internal inline fun MessageSvcPbSendMsg.createToStranger(
client: QQAndroidClient,
stranger: Stranger,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToStrangerImpl) -> Unit
crossinline sourceCallback: (OnlineMessageSourceToStrangerImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val source = MessageSourceToStrangerImpl(
val source = OnlineMessageSourceToStrangerImpl(
internalIds = intArrayOf(Random.nextInt().absoluteValue),
sender = client.bot,
target = stranger,
@ -406,7 +406,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
client: QQAndroidClient,
qq: Friend,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
): List<OutgoingPacket> {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
@ -425,13 +425,13 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
group: Group,
message: MessageChain,
isForward: Boolean,
crossinline sourceCallback: (MessageSourceToGroupImpl) -> Unit
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit
): OutgoingPacket {
contract {
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
}
val messageRandom = Random.nextInt().absoluteValue
val source = MessageSourceToGroupImpl(
val source = OnlineMessageSourceToGroupImpl(
group,
internalIds = intArrayOf(messageRandom),
sender = client.bot,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* 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.
@ -15,6 +15,7 @@ import kotlinx.serialization.serializer
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.internal.message.MarketFaceImpl
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.message.MessageSerializer
import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
@ -22,11 +23,13 @@ import kotlin.test.assertEquals
internal class MessageSerializationTest {
@Suppress("DEPRECATION_ERROR")
private val module get() = Message.Serializer.serializersModule
private val module
get() = MessageSerializer.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 {
@ -38,7 +41,14 @@ internal class MessageSerializationTest {
}
private inline fun <reified T : Any> testSerialization(t: T, serializer: KSerializer<T> = module.serializer()) {
val deserialized = t.serialize(serializer).deserialize(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 AssertionError("Failed to serialize $t", it)
}
assertEquals(
t,
deserialized,
@ -78,7 +88,6 @@ internal class MessageSerializationTest {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
private val testConstrainSingleMessageInstances: Array<out ConstrainSingle> = arrayOf(
LongMessage("content", "resId"),
Mirai.constructMessageSource(
1L,
MessageSourceKind.FRIEND,
@ -100,7 +109,12 @@ internal class MessageSerializationTest {
}
@Test
fun `test serialize each message contents`() {
fun `test polymorphic serialization`() {
}
@Test
fun `test contextual serialization`() {
for (message in testMessageContentInstances) {
testSerialization(message, module.serializer(message.javaClass))
}