diff --git a/mirai-core/src/commonMain/kotlin/message/image/AbstractImage.kt b/mirai-core/src/commonMain/kotlin/message/image/AbstractImage.kt index 18ace571da..2fb7f0f7f3 100644 --- a/mirai-core/src/commonMain/kotlin/message/image/AbstractImage.kt +++ b/mirai-core/src/commonMain/kotlin/message/image/AbstractImage.kt @@ -69,5 +69,8 @@ internal sealed class FriendImage : AbstractImage() // moved from mirai-core-api since 2.11 internal sealed class GroupImage : AbstractImage() +// NT Image +internal sealed class NewTechImage : AbstractImage() + private val imageLogger: MiraiLogger by lazy { MiraiLogger.Factory.create(Image::class, "Image") } internal val Image.Key.logger get() = imageLogger \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/image/OnlineImage.kt b/mirai-core/src/commonMain/kotlin/message/image/OnlineImage.kt index 79f6d9e489..a32f527f10 100644 --- a/mirai-core/src/commonMain/kotlin/message/image/OnlineImage.kt +++ b/mirai-core/src/commonMain/kotlin/message/image/OnlineImage.kt @@ -21,6 +21,7 @@ import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.ImageType import net.mamoe.mirai.utils.generateImageId import net.mamoe.mirai.utils.generateImageIdFromResourceId +import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.structureToString internal sealed interface OnlineImage : Image, ConstOriginUrlAware { @@ -137,6 +138,51 @@ internal class OnlineGroupImageImpl( } } +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +internal sealed class OnlineNewTechImage : NewTechImage(), OnlineImage + +@Suppress("SERIALIZER_TYPE_INCOMPATIBLE") +@Serializable(with = OnlineNewTechImageImpl.Serializer::class) +internal class OnlineNewTechImageImpl( + internal val commonElem: ImMsgBody.CommonElem, +) : OnlineNewTechImage() { + + private val delegate = commonElem.pbElem.loadAs(ImMsgBody.NewTechImageInfo.serializer()) + + object Serializer : Image.FallbackSerializer("OnlineNewTechImage") + + override val md5 = delegate.info.msgInfo.imageInfo.md5.hexToBytes() + + private val senderMeta = delegate.meta.main.friendMeta ?: delegate.meta.main.groupMeta!! + override val size: Long get() = delegate.info.msgInfo.imageInfo.size + override val width: Int + get() = delegate.info.msgInfo.imageInfo.imageWidth + override val height: Int + get() = delegate.info.msgInfo.imageInfo.imageHeight + override val imageType: ImageType + get() = OnlineImageIds.speculateImageType( + delegate.info.msgInfo.imageInfo.filePath, + delegate.info.msgInfo.imageInfo.imageType.type + ) + + override val imageId: String = generateImageId( + md5, + OnlineImageIds.speculateImageTypeNameFromFilePath(delegate.info.msgInfo.imageInfo.filePath) + ).takeIf { + Image.IMAGE_ID_REGEX.matches(it) + } ?: generateImageId(md5) + + override val originUrl: String + get() = if (senderMeta.origUrl.isBlank()) { + gchatImageUrlByImageId(imageId) + } else "http://" + delegate.info.noKeyDownloadInfo.domain + senderMeta.origUrl + + override val isEmoji: Boolean by lazy { + delegate.meta.main.isEmoji == 1 || delegate.meta.main.displayStr == "[动画表情]" + + } +} + private object OnlineImageIds { fun speculateImageType(filePath: String, imageTypeInt: Int): ImageType { diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ImageProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ImageProtocol.kt index 91c4cccea7..9577028c56 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ImageProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ImageProtocol.kt @@ -49,6 +49,7 @@ internal class ImageProtocol : MessageProtocol() { add(MessageSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer())) add(MessageSerializer(OnlineFriendImageImpl::class, OnlineFriendImageImpl.serializer())) add(MessageSerializer(OnlineGroupImageImpl::class, OnlineGroupImageImpl.serializer())) + add(MessageSerializer(OnlineNewTechImageImpl::class, OnlineNewTechImageImpl.serializer())) } } @@ -88,6 +89,9 @@ internal class ImageProtocol : MessageProtocol() { } } } + data.commonElem != null && data.commonElem.serviceType == 48 -> { + collect(OnlineNewTechImageImpl(data.commonElem)) + } else -> { markNotConsumed() } @@ -129,6 +133,9 @@ internal class ImageProtocol : MessageProtocol() { collect(ImMsgBody.Elem(customFace = data.toJceData().toCustomFace())) } } + is OnlineNewTechImageImpl -> { + collect(ImMsgBody.Elem(commonElem = data.commonElem)) + } } } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt index 5a047cf22d..d87c930199 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt @@ -696,6 +696,140 @@ internal class ImMsgBody : ProtoBuf { @ProtoNumber(56) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY, ) : ProtoBuf + @Serializable + internal class NewTechImageType( + @ProtoNumber(1) @JvmField val i: Int = 0, + @ProtoNumber(2) @JvmField val type: Int = 0, + @ProtoNumber(3) @JvmField val j: Int = 0, + @ProtoNumber(4) @JvmField val k: Int = 0, + ) : ProtoBuf + + @Serializable + internal class NewTechImageFileInfo( + @ProtoNumber(1) @JvmField val size: Long = 0L, + @ProtoNumber(2) @JvmField val md5: String = "", + @ProtoNumber(3) @JvmField val sha1: String = "", + @ProtoNumber(4) @JvmField val filePath: String = "", + @ProtoNumber(5) @JvmField val imageType: NewTechImageType, + @ProtoNumber(6) @JvmField val imageWidth: Int = 0, + @ProtoNumber(7) @JvmField val imageHeight: Int = 0, + @ProtoNumber(8) @JvmField val i: Int = 0, + @ProtoNumber(9) @JvmField val j: Int = 0, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMsgInfo( + @ProtoNumber(1) @JvmField val imageInfo: NewTechImageFileInfo, + @ProtoNumber(2) @JvmField val fileId: String = "", + @ProtoNumber(3) @JvmField val i: Int = 0, + @ProtoNumber(4) @JvmField val timestamp: Long = 0L, + @ProtoNumber(5) @JvmField val friendOrGroup: Int = 0, + @ProtoNumber(6) @JvmField val j: Int = 0, + ) : ProtoBuf + + @Serializable + internal class NewTechImageSpec( + @ProtoNumber(1) @JvmField val origin: String = "&spec=0", + @ProtoNumber(2) @JvmField val large: String = "&spec=720", + @ProtoNumber(3) @JvmField val small: String = "&spec=198", + ) : ProtoBuf + + @Serializable + internal class NewTechImageNoKeyDownloadInfo( + @ProtoNumber(1) @JvmField val noKeyUrl: String = "", + @ProtoNumber(2) @JvmField val spec: NewTechImageSpec, + @ProtoNumber(3) @JvmField val domain: String = "multimedia.nt.qq.com.cn", + ) : ProtoBuf + + @Serializable + internal class NewTechImageGroupInfo( + @ProtoNumber(1) @JvmField val groupId: Long = 0L, + ) : ProtoBuf + + @Serializable + internal class NewTechImageFriendInfo( + @ProtoNumber(1) @JvmField val i: Int = 0, + @ProtoNumber(2) @JvmField val j: String = "", + ) : ProtoBuf + + @Serializable + internal class NewTechImageSenderInfo( + @ProtoNumber(101) @JvmField val i: Int = 0, + @ProtoNumber(102) @JvmField val j: Int = 0, + @ProtoNumber(200) @JvmField val k: Int = 0, + @ProtoNumber(201) @JvmField val friendInfo: NewTechImageFriendInfo?, + @ProtoNumber(202) @JvmField val groupInfo: NewTechImageGroupInfo?, + ) : ProtoBuf + + @Serializable + internal class NewTechImageInfoMain( + @ProtoNumber(1) @JvmField val msgInfo: NewTechImageMsgInfo, + @ProtoNumber(2) @JvmField val noKeyDownloadInfo: NewTechImageNoKeyDownloadInfo, + @ProtoNumber(5) @JvmField val k: Int = 0, + @ProtoNumber(6) @JvmField val senderInfo: NewTechImageSenderInfo, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfoSenderMetaSenderUnknown( + @ProtoNumber(1) @JvmField val i: Int = 0, + @ProtoNumber(2) @JvmField val j: ByteArray = EMPTY_BYTE_ARRAY, + @ProtoNumber(3) @JvmField val k: Int = 0, + @ProtoNumber(4) @JvmField val l: Int = 0, + @ProtoNumber(5) @JvmField val m: Int = 0, + @ProtoNumber(7) @JvmField val n: ByteArray = EMPTY_BYTE_ARRAY, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfoSenderMeta( + @ProtoNumber(1) @JvmField val i: Int = 0, + @ProtoNumber(3) @JvmField val j: Int = 0, + @ProtoNumber(4) @JvmField val k: Int = 0, + @ProtoNumber(9) @JvmField val displayStr: String = "", + @ProtoNumber(10) @JvmField val l: Int = 0, + @ProtoNumber(12) @JvmField val m: ByteArray? = EMPTY_BYTE_ARRAY, + @ProtoNumber(18) @JvmField val n: ByteArray? = EMPTY_BYTE_ARRAY, + @ProtoNumber(19) @JvmField val o: ByteArray? = EMPTY_BYTE_ARRAY, + @ProtoNumber(20) @JvmField val friendUnknown: NewTechImageMetaInfoSenderMetaSenderUnknown?, + @ProtoNumber(21) @JvmField val groupUnknown: NewTechImageMetaInfoSenderMetaSenderUnknown?, + @ProtoNumber(30) @JvmField val origUrl: String = "", + @ProtoNumber(31) @JvmField val md5Upper: String = "", + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfoMain( + @ProtoNumber(1) @JvmField val isEmoji: Int = 0, + @ProtoNumber(2) @JvmField val displayStr: String = "", + @ProtoNumber(11) @JvmField val friendMeta: NewTechImageMetaInfoSenderMeta?, + @ProtoNumber(12) @JvmField val groupMeta: NewTechImageMetaInfoSenderMeta?, + @ProtoNumber(1001) @JvmField val i: Int = 0, + @ProtoNumber(1002) @JvmField val j: Int = 0, + @ProtoNumber(1003) @JvmField val k: Int? = 0, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfoUnknown2( + @ProtoNumber(3) @JvmField val i: ByteArray = EMPTY_BYTE_ARRAY, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfoUnknown3( + @ProtoNumber(11) @JvmField val i: ByteArray = EMPTY_BYTE_ARRAY, + @ProtoNumber(12) @JvmField val j: ByteArray = EMPTY_BYTE_ARRAY, + ) : ProtoBuf + + @Serializable + internal class NewTechImageMetaInfo( + @ProtoNumber(1) @JvmField val main: NewTechImageMetaInfoMain, + @ProtoNumber(2) @JvmField val unknown1: NewTechImageMetaInfoUnknown2, + @ProtoNumber(3) @JvmField val unknown2: NewTechImageMetaInfoUnknown3, + ) : ProtoBuf + + @Serializable + internal class NewTechImageInfo( + @ProtoNumber(1) @JvmField val info: NewTechImageInfoMain, + @ProtoNumber(2) @JvmField val meta: NewTechImageMetaInfo, + ) : ProtoBuf + interface NotOnlineImageOrCustomFace { val thumbUrl: String val origUrl: String