diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index 8edb3e22c..980318085 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -156,6 +156,7 @@ internal class GroupImpl( is ImgStore.GroupPicUp.Response.FileExists -> { val resourceId = resource.calculateResourceId() return OfflineGroupImage(imageId = resourceId) + .also { it.fileId = response.fileId.toInt() } .also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() } } is ImgStore.GroupPicUp.Response.RequireUpload -> { @@ -169,6 +170,7 @@ internal class GroupImpl( ) return OfflineGroupImage(imageId = resource.calculateResourceId()) + .also { it.fileId = response.fileId.toInt() } .also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() } } } diff --git a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt index cc78d45cf..e10c2c697 100644 --- a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt @@ -25,11 +25,11 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.* import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToGroup +import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.castOrNull import net.mamoe.mirai.utils.currentTimeSeconds -import java.lang.UnsupportedOperationException /** * 通用处理器 @@ -105,7 +105,7 @@ internal abstract class SendMessageHandler { */ suspend fun sendMessagePacket( originalMessage: Message, - transformedMessage: Message, + transformedMessage: MessageChain, finalMessage: MessageChain, step: SendMessageStep, ): MessageReceipt { @@ -125,10 +125,10 @@ internal abstract class SendMessageHandler { if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) { return when (step) { SendMessageStep.FIRST -> { - sendMessage(originalMessage, transformedMessage, SendMessageStep.LONG_MESSAGE) + sendMessageImpl(originalMessage, transformedMessage, SendMessageStep.LONG_MESSAGE) } SendMessageStep.LONG_MESSAGE -> { - sendMessage(originalMessage, transformedMessage, SendMessageStep.FRAGMENTED) + sendMessageImpl(originalMessage, transformedMessage, SendMessageStep.FRAGMENTED) } else -> { @@ -219,6 +219,8 @@ internal abstract class SendMessageHandler { ) } + open suspend fun preConversionTransformedMessage(message: Message): Message = message + open suspend fun conversionMessageChain(chain: MessageChain): MessageChain = chain open suspend fun postTransformActions(chain: MessageChain) { @@ -255,15 +257,33 @@ internal suspend fun SendMessageHandler.transformSpecialMessage } /** - * Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed (sendMessagePacket) + * Send a message, and covert messages + * + * Don't recall this function. */ internal suspend fun SendMessageHandler.sendMessage( originalMessage: Message, transformedMessage: Message, step: SendMessageStep, +): MessageReceipt = sendMessageImpl( + originalMessage, + conversionMessageChain( + transformSpecialMessages( + preConversionTransformedMessage(transformedMessage) + ) + ), + step +) + +/** + * Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed (sendMessagePacket) + */ +internal suspend fun SendMessageHandler.sendMessageImpl( + originalMessage: Message, + transformedMessage: MessageChain, + step: SendMessageStep, ): MessageReceipt { // Result cannot be in interface. - val chain = transformSpecialMessages(transformedMessage) - .convertToLongMessageIfNeeded(step) + val chain = transformedMessage.convertToLongMessageIfNeeded(step) chain.findIsInstance()?.source?.ensureSequenceIdAvailable() @@ -314,11 +334,19 @@ internal class GroupSendMessageHandler( override val senderName: String get() = contact.botAsMember.nameCardOrNick - override suspend fun postTransformActions(chain: MessageChain) { - chain.asSequence().filterIsInstance().forEach { image -> - contact.updateFriendImageForGroupMessage(image) + override suspend fun conversionMessageChain(chain: MessageChain): MessageChain = chain.map { element -> + when (element) { + is OfflineGroupImage -> { + contact.fixImageFileId(element) + element + } + is FriendImage -> { + contact.updateFriendImageForGroupMessage(element) + } + else -> element } - } + }.toMessageChain() + override suspend fun constructSourceFromMusicShareResponse( finalMessage: MessageChain, @@ -341,18 +369,55 @@ internal class GroupSendMessageHandler( } companion object { + private suspend fun GroupImpl.fixImageFileId(image: OfflineGroupImage) { + if (image.fileId == null) { + val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp( + bot.client, + uin = bot.id, + groupCode = this.id, + md5 = image.md5, + size = 1, + ).sendAndExpect(bot) + + when (response) { + is ImgStore.GroupPicUp.Response.Failed -> { + image.fileId = 0 // Failed + } + is ImgStore.GroupPicUp.Response.FileExists -> { + image.fileId = response.fileId.toInt() + } + is ImgStore.GroupPicUp.Response.RequireUpload -> { + image.fileId = response.fileId.toInt() + } + } + } + } + /** * Ensures server holds the cache */ - private suspend fun GroupImpl.updateFriendImageForGroupMessage(image: FriendImage) { + private suspend fun GroupImpl.updateFriendImageForGroupMessage(image: FriendImage): OfflineGroupImage { bot.network.run { - ImgStore.GroupPicUp( + val response = ImgStore.GroupPicUp( bot.client, uin = bot.id, groupCode = id, md5 = image.md5, size = if (image is OnlineFriendImageImpl) image.delegate.fileLen else 0 ).sendAndExpect() + return OfflineGroupImage(image.imageId).also { img -> + when (response) { + is ImgStore.GroupPicUp.Response.FileExists -> { + img.fileId = response.fileId.toInt() + } + is ImgStore.GroupPicUp.Response.RequireUpload -> { + img.fileId = response.fileId.toInt() + } + is ImgStore.GroupPicUp.Response.Failed -> { + img.fileId = 0 + } + } + } } } } diff --git a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt index 5e942efa2..d2ba26cf5 100644 --- a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt @@ -178,6 +178,7 @@ internal fun ImMsgBody.CustomFace.toNotOnlineImage(): ImMsgBody.NotOnlineImage { @Suppress("DEPRECATION") internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace { return ImMsgBody.CustomFace( + fileId = this.fileId ?: 0, filePath = this.imageId, picMd5 = this.md5, flag = ByteArray(4), @@ -185,7 +186,9 @@ internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace { //_400Url = "/gchatpic_new/000000000/1041235568-2195821338-01E9451B70EDEAE3B37C101F1EEBF5B5/400?term=2", //_400Width = 351, oldData = oldData, - // pbReserve = CustomFaceExtPb.ResvAttr().toByteArray(CustomFaceExtPb.ResvAttr.serializer()) +// pbReserve = "08 00 10 00 32 00 50 00 78 08".autoHexToBytes(), +// useful = 1, + // pbReserve = CustomFaceExtPb.ResvAttr().toByteArray(CustomFaceExtPb.ResvAttr.serializer()) ) } @@ -258,6 +261,9 @@ internal interface OfflineImage : Image internal data class OfflineGroupImage( override val imageId: String ) : GroupImage(), OfflineImage, DeferredOriginUrlAware { + @Transient + internal var fileId: Int? = null + object Serializer : Image.FallbackSerializer("OfflineGroupImage") override fun getUrl(bot: Bot): String {