From de8e6469e073238edeb73c0c0c809457ae90924d Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 2 Feb 2021 14:51:29 +0800
Subject: [PATCH] Support long message refinement, close #692

---
 .../src/commonMain/kotlin/QQAndroidBot.kt     |  1 +
 .../commonMain/kotlin/contact/AbstractUser.kt |  4 +-
 .../commonMain/kotlin/contact/GroupImpl.kt    |  2 +-
 .../kotlin/message/LongMessageInternal.kt     | 99 +++++++++++++++++--
 .../kotlin/message/ReceiveMessageHandler.kt   | 20 +++-
 .../kotlin/network/highway/Highway.kt         | 59 ++++++++++-
 .../protocol/data/proto/MsgTransmit.kt        |  8 +-
 .../network/protocol/data/proto/MultiMsg.kt   |  4 +-
 .../network/protocol/packet/chat/MultiMsg.kt  | 23 ++++-
 .../chat/receive/MessageSvc.PbGetMsg.kt       | 28 +++---
 .../chat/receive/OnlinePush.PbPushGroupMsg.kt |  7 +-
 11 files changed, 215 insertions(+), 40 deletions(-)

diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
index 6756d319c..ec53ede39 100644
--- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
+++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
@@ -57,6 +57,7 @@ internal class QQAndroidBot constructor(
     configuration: BotConfiguration
 ) : AbstractBot<QQAndroidBotNetworkHandler>(configuration, account.id) {
     var client: QQAndroidClient = initClient()
+    private set
 
     fun initClient(): QQAndroidClient {
         client = QQAndroidClient(
diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
index 0b9da3e04..579a71ee3 100644
--- a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
@@ -22,7 +22,7 @@ import net.mamoe.mirai.internal.network.highway.ChannelKind
 import net.mamoe.mirai.internal.network.highway.Highway
 import net.mamoe.mirai.internal.network.highway.ResourceKind.PRIVATE_IMAGE
 import net.mamoe.mirai.internal.network.highway.postImage
-import net.mamoe.mirai.internal.network.highway.tryServers
+import net.mamoe.mirai.internal.network.highway.tryServersUpload
 import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
 import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
 import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
@@ -148,7 +148,7 @@ internal abstract class AbstractUser(
                     )
                 }.recoverCatchingSuppressed {
                     // try upload by http on provided servers
-                    tryServers(
+                    tryServersUpload(
                         bot = bot,
                         servers = resp.serverIp.zip(resp.serverPort),
                         resourceSize = resource.size,
diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
index 8ce574f5f..dd9ce345b 100644
--- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
@@ -192,7 +192,7 @@ internal class GroupImpl(
             }.recoverCatchingSuppressed {
                 when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect()) {
                     is PttStore.GroupPttUp.Response.RequireUpload -> {
-                        tryServers(
+                        tryServersUpload(
                             bot,
                             resp.uploadIpList.zip(resp.uploadPortList),
                             resource.size,
diff --git a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
index 1c51b9a29..1f26769a3 100644
--- a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
@@ -9,24 +9,111 @@
 
 package net.mamoe.mirai.internal.message
 
-import net.mamoe.mirai.message.data.AbstractPolymorphicMessageKey
-import net.mamoe.mirai.message.data.AbstractServiceMessage
-import net.mamoe.mirai.message.data.ServiceMessage
-import net.mamoe.mirai.utils.safeCast
+import io.ktor.client.request.*
+import kotlinx.io.core.discardExact
+import kotlinx.io.core.readBytes
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.contact.Group
+import net.mamoe.mirai.internal.asQQAndroidBot
+import net.mamoe.mirai.internal.network.highway.ChannelKind
+import net.mamoe.mirai.internal.network.highway.ResourceKind
+import net.mamoe.mirai.internal.network.highway.tryDownload
+import net.mamoe.mirai.internal.network.highway.tryServersDownload
+import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
+import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit
+import net.mamoe.mirai.internal.network.protocol.packet.chat.MultiMsg
+import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
+import net.mamoe.mirai.internal.utils.crypto.TEA
+import net.mamoe.mirai.internal.utils.io.serialization.loadAs
+import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.utils.*
 
 // internal runtime value, not serializable
 internal data class LongMessageInternal internal constructor(override val content: String, val resId: String) :
-    AbstractServiceMessage() {
+    AbstractServiceMessage(), RefinableMessage {
     override val serviceId: Int get() = 35
 
+    override suspend fun refine(contact: Contact, context: MessageChain): Message {
+        val bot = contact.bot.asQQAndroidBot()
+        when (val resp = MultiMsg.ApplyDown(bot.client, 1, resId, 1).sendAndExpect(bot)) {
+            is MultiMsg.ApplyDown.Response.RequireDownload -> {
+                val http = Mirai.Http
+                val origin = resp.origin
+
+                val data = if (origin.msgExternInfo?.channelType == 2) {
+                    tryDownload(
+                        bot = bot,
+                        host = "https://ssl.htdata.qq.com",
+                        port = 0,
+                        resourceKind = ResourceKind.LONG_MESSAGE,
+                        channelKind = ChannelKind.HTTP
+                    ) { host, port ->
+                        http.get<ByteArray>("$host${origin.thumbDownPara}:$port")
+                    }
+                } else tryServersDownload(
+                    bot = bot,
+                    servers = origin.uint32DownIp.zip(origin.uint32DownPort),
+                    resourceKind = ResourceKind.LONG_MESSAGE,
+                    channelKind = ChannelKind.HTTP
+                ) { ip, port ->
+                    http.get("http://$ip${origin.thumbDownPara}:$port")
+                }
+
+                val body = data.read {
+                    check(readByte() == 40.toByte()) {
+                        "bad data while MultiMsg.ApplyDown: ${data.toUHexString()}"
+                    }
+                    val headLength = readInt()
+                    val bodyLength = readInt()
+                    discardExact(headLength)
+                    readBytes(bodyLength)
+                }
+
+                val decrypted = TEA.decrypt(body, origin.msgKey)
+                val longResp =
+                    decrypted.loadAs(LongMsg.RspBody.serializer())
+
+                val down = longResp.msgDownRsp.single()
+                check(down.result == 0) {
+                    "Message download failed, result=${down.result}, resId=${down.msgResid}, msgContent=${down.msgContent.toUHexString()}"
+                }
+
+                val content = down.msgContent.ungzip()
+                val transmit = content.loadAs(MsgTransmit.PbMultiMsgTransmit.serializer())
+
+                val source = context.source
+                return transmit.msg.toMessageChainNoSource(bot.id, contact.castOrNull<Group>()?.id ?: 0, source.kind)
+            }
+            MultiMsg.ApplyDown.Response.MessageTooLarge -> {
+                error("Message is too large and cannot download")
+            }
+        }
+    }
+
     companion object Key :
         AbstractPolymorphicMessageKey<ServiceMessage, LongMessageInternal>(ServiceMessage, { it.safeCast() })
 }
 
 // internal runtime value, not serializable
-internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage() {
+internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage(), RefinableMessage {
     override val serviceId: Int get() = 35
 
+    override suspend fun refine(contact: Contact, context: MessageChain): Message {
+        // val bot = contact.bot.asQQAndroidBot()
+        // TODO: 2021/2/2 Support forward message refinement
+        // https://github.com/mamoe/mirai/issues/623
+        return this
+    }
+
     companion object Key :
         AbstractPolymorphicMessageKey<ServiceMessage, ForwardMessageInternal>(ServiceMessage, { it.safeCast() })
+}
+
+internal interface RefinableMessage : SingleMessage {
+
+    suspend fun refine(
+        contact: Contact,
+        context: MessageChain,
+    ): Message?
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt
index eab697c50..1b1e68f7d 100644
--- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt
@@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.message
 import kotlinx.io.core.discardExact
 import kotlinx.io.core.readUInt
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
 import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.joinToMessageChain
 import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.toVoice
@@ -410,4 +411,21 @@ private object ReceiveMessageTransformer {
         format,
         kotlinx.io.core.String(downPara)
     )
-}
\ No newline at end of file
+}
+
+/**
+ * 解析 [ForwardMessageInternal], [LongMessageInternal]
+ */
+internal suspend fun MessageChain.refine(contact: Contact): MessageChain {
+    if (none { it is RefinableMessage }) return this
+    val builder = MessageChainBuilder(this.size)
+    for (singleMessage in this) {
+        if (singleMessage is RefinableMessage) {
+            val v = singleMessage.refine(contact, this)
+            if (v != null) builder.add(v)
+        } else {
+            builder.add(singleMessage)
+        }
+    }
+    return builder.build()
+}
diff --git a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt
index 799e74f98..68144652d 100644
--- a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt
+++ b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt
@@ -38,6 +38,8 @@ import kotlin.math.roundToInt
 import kotlin.time.measureTime
 
 internal object Highway {
+
+
     @Suppress("ArrayInDataClass")
     data class BdhUploadResponse(
         var extendInfo: ByteArray? = null,
@@ -55,7 +57,7 @@ internal object Highway {
     ): BdhUploadResponse {
         val bdhSession = bot.client.bdhSession.await() // no need to care about timeout. proceed by bot init
 
-        return tryServers(
+        return tryServersUpload(
             bot = bot,
             servers = if (tryOnce) listOf(bdhSession.ssoAddresses.random()) else bdhSession.ssoAddresses,
             resourceSize = resource.size,
@@ -113,7 +115,7 @@ internal enum class ChannelKind(
     override fun toString(): String = display
 }
 
-internal suspend inline fun <reified R> tryServers(
+internal suspend inline fun <reified R> tryServersUpload(
     bot: QQAndroidBot,
     servers: Collection<Pair<Int, Int>>,
     resourceSize: Long,
@@ -147,6 +149,59 @@ internal suspend inline fun <reified R> tryServers(
     resp as R
 }
 
+internal suspend inline fun <reified R> tryServersDownload(
+    bot: QQAndroidBot,
+    servers: Collection<Pair<Int, Int>>,
+    resourceKind: ResourceKind,
+    channelKind: ChannelKind,
+    crossinline implOnEachServer: suspend (ip: String, port: Int) -> R
+) = servers.retryWithServers(
+    5000,
+    onFail = { throw IllegalStateException("cannot download $resourceKind, failed on all servers.", it) }
+) { ip, port ->
+    tryUploadImplEach(bot, channelKind, resourceKind, ip, port, implOnEachServer)
+}
+
+internal suspend inline fun <reified R> tryDownload(
+    bot: QQAndroidBot,
+    host: String,
+    port: Int,
+    resourceKind: ResourceKind,
+    channelKind: ChannelKind,
+    crossinline implOnEachServer: suspend (ip: String, port: Int) -> R
+) = runCatching {
+    tryUploadImplEach(bot, channelKind, resourceKind, host, port, implOnEachServer)
+}.getOrElse { throw IllegalStateException("cannot upload $resourceKind, failed on all servers.", it) }
+
+private suspend inline fun <reified R> tryUploadImplEach(
+    bot: QQAndroidBot,
+    channelKind: ChannelKind,
+    resourceKind: ResourceKind,
+    host: String,
+    port: Int,
+    crossinline implOnEachServer: suspend (ip: String, port: Int) -> R
+): R {
+    bot.network.logger.verbose {
+        "[${channelKind}] Downloading $resourceKind to ${host}:$port"
+    }
+
+    var resp: R? = null
+    runCatching {
+        resp = implOnEachServer(host, port)
+    }.onFailure {
+        bot.network.logger.verbose {
+            "[${channelKind}] Downloading $resourceKind to ${host}:$port failed: $it"
+        }
+        throw it
+    }
+
+    bot.network.logger.verbose {
+        "[${channelKind}] Downloading $resourceKind: succeed"
+    }
+
+    return resp as R
+}
+
 internal suspend fun ChunkedFlowSession<ByteReadPacket>.sendSequentially(
     socket: PlatformSocket,
     respCallback: (resp: CSDataHighwayHead.RspDataHighwayHead) -> Unit = {}
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt
index 1a4fd8a61..5410cf75a 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt
@@ -16,19 +16,19 @@ import net.mamoe.mirai.internal.utils.io.ProtoBuf
 
 internal class MsgTransmit : ProtoBuf {
     @Serializable
-internal class PbMultiMsgItem(
+    internal class PbMultiMsgItem(
         @ProtoNumber(1) @JvmField val fileName: String = "",
         @ProtoNumber(2) @JvmField val buffer: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
     @Serializable
-internal class PbMultiMsgNew(
+    internal class PbMultiMsgNew(
         @ProtoNumber(1) @JvmField val msg: List<MsgComm.Msg> = emptyList()
     ) : ProtoBuf
 
     @Serializable
-internal class PbMultiMsgTransmit(
+    internal class PbMultiMsgTransmit(
         @ProtoNumber(1) @JvmField val msg: List<MsgComm.Msg> = emptyList(),
-        @ProtoNumber(2) @JvmField val pbItemList: List<MsgTransmit.PbMultiMsgItem> = emptyList()
+        @ProtoNumber(2) @JvmField val pbItemList: List<PbMultiMsgItem> = emptyList()
     ) : ProtoBuf
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt
index 378c87995..24c32811c 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt
@@ -31,11 +31,11 @@ internal class MultiMsg : ProtoBuf {
     @Serializable
     internal class MultiMsgApplyDownRsp(
         @ProtoNumber(1) @JvmField val result: Int = 0,
-        @ProtoNumber(2) @JvmField val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
+        @ProtoNumber(2) @JvmField val thumbDownPara: String = "",
         @ProtoNumber(3) @JvmField val msgKey: ByteArray = EMPTY_BYTE_ARRAY,
         @ProtoNumber(4) @JvmField val uint32DownIp: List<Int> = emptyList(),
         @ProtoNumber(5) @JvmField val uint32DownPort: List<Int> = emptyList(),
-        @ProtoNumber(6) @JvmField val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
+        @ProtoNumber(6) @JvmField val msgResid: String = "",
         @ProtoNumber(7) @JvmField val msgExternInfo: ExternMsg? = null,
         @ProtoNumber(8) @JvmField val bytesDownIpV6: List<ByteArray> = emptyList(),
         @ProtoNumber(9) @JvmField val uint32DownV6Port: List<Int> = emptyList()
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt
index 6f62fa84c..3bd6639cd 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt
@@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.network.protocol.packet.chat
 import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.contact.SendMessageHandler
+import net.mamoe.mirai.internal.message.contextualBugReportException
 import net.mamoe.mirai.internal.message.toRichTextElems
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.QQAndroidClient
@@ -152,8 +153,13 @@ internal class MultiMsg {
         }
     }
 
-    object ApplyDown: OutgoingPacketFactory<ApplyDown.Response>("MultiMsg.ApplyDown") {
+    object ApplyDown : OutgoingPacketFactory<ApplyDown.Response>("MultiMsg.ApplyDown") {
         sealed class Response : Packet {
+            class RequireDownload(
+                val origin: MultiMsg.MultiMsgApplyDownRsp
+            ) : Response() {
+                override fun toString(): String = "MultiMsg.ApplyDown.Response"
+            }
 
             object MessageTooLarge : Response()
         }
@@ -171,7 +177,7 @@ internal class MultiMsg {
                     buildVer = "8.2.0.1296",
                     multimsgApplydownReq = listOf(
                         MultiMsg.MultiMsgApplyDownReq(
-                            msgResid =  resId,
+                            msgResid = resId,
                             msgType = msgType,
                         )
                     ),
@@ -185,7 +191,18 @@ internal class MultiMsg {
         }
 
         override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
-            return Response.MessageTooLarge
+            val body = readProtoBuf(MultiMsg.RspBody.serializer())
+            val response = body.multimsgApplydownRsp.first()
+            return when (response.result) {
+                0 -> Response.RequireDownload(response)
+                193 -> Response.MessageTooLarge
+                //1 -> Response.OK(resId = response.msgResid)
+                else -> throw contextualBugReportException(
+                    "MultiMsg.ApplyDown",
+                    response._miraiContentToString(),
+                    additional = "Decode failure result=${response.result}"
+                )
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
index e82339dff..9c2440a7f 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
@@ -30,6 +30,7 @@ import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.contact.*
 import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
+import net.mamoe.mirai.internal.message.refine
 import net.mamoe.mirai.internal.message.toMessageChainOnline
 import net.mamoe.mirai.internal.network.MultiPacket
 import net.mamoe.mirai.internal.network.Packet
@@ -49,6 +50,8 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
 import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
 import net.mamoe.mirai.message.data.MessageSourceKind
+import net.mamoe.mirai.message.data.MessageSourceKind.STRANGER
+import net.mamoe.mirai.message.data.MessageSourceKind.TEMP
 import net.mamoe.mirai.message.data.PlainText
 import net.mamoe.mirai.message.data.buildMessageChain
 import net.mamoe.mirai.utils.*
@@ -389,29 +392,22 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot, fromSync: Boolean
                 friend.checkIsFriendImpl()
                 friend.lastMessageSequence.loop {
                     //我也不知道为什么要这样写,但它就是能跑
-                    return if (friend.lastMessageSequence.value != msgHead.msgSeq && friend.lastMessageSequence.compareAndSet(
-                            it,
-                            msgHead.msgSeq
-                        ) && contentHead?.autoReply != 1
+                    return if (friend.lastMessageSequence.value != msgHead.msgSeq
+                        && friend.lastMessageSequence.compareAndSet(it, msgHead.msgSeq)
+                        && contentHead?.autoReply != 1
                     ) {
                         val msgs = friend.friendPkgMsgParsingCache.tryMerge(this)
                         if (msgs.isNotEmpty()) {
                             if (fromSync) {
                                 FriendMessageSyncEvent(
                                     friend,
-                                    msgs.toMessageChainOnline(
-                                        bot = bot, groupIdOrZero = 0,
-                                        messageSourceKind = MessageSourceKind.FRIEND
-                                    ),
+                                    msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND).refine(friend),
                                     msgHead.msgTime
                                 )
                             } else {
                                 FriendMessageEvent(
                                     friend,
-                                    msgs.toMessageChainOnline(
-                                        bot = bot, groupIdOrZero = 0,
-                                        MessageSourceKind.FRIEND
-                                    ),
+                                    msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND).refine(friend),
                                     msgHead.msgTime
                                 )
                             }
@@ -430,13 +426,13 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot, fromSync: Boolean
                         if (fromSync) {
                             StrangerMessageSyncEvent(
                                 stranger,
-                                listOf(this).toMessageChainOnline(bot, groupIdOrZero = 0, MessageSourceKind.STRANGER),
+                                listOf(this).toMessageChainOnline(bot, 0, STRANGER).refine(stranger),
                                 msgHead.msgTime
                             )
                         } else {
                             StrangerMessageEvent(
                                 stranger,
-                                listOf(this).toMessageChainOnline(bot, groupIdOrZero = 0, MessageSourceKind.STRANGER),
+                                listOf(this).toMessageChainOnline(bot, 0, STRANGER).refine(stranger),
                                 msgHead.msgTime
                             )
                         }
@@ -510,13 +506,13 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot, fromSync: Boolean
                         return if (fromSync) {
                             GroupTempMessageSyncEvent(
                                 member,
-                                listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
+                                listOf(this).toMessageChainOnline(bot, 0, TEMP).refine(member),
                                 msgHead.msgTime
                             )
                         } else {
                             GroupTempMessageEvent(
                                 member,
-                                listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
+                                listOf(this).toMessageChainOnline(bot, 0, TEMP).refine(member),
                                 msgHead.msgTime
                             )
                         }
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
index 982de268b..56809219d 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
@@ -23,6 +23,7 @@ import net.mamoe.mirai.event.events.GroupMessageSyncEvent
 import net.mamoe.mirai.event.events.MemberCardChangeEvent
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.contact.*
+import net.mamoe.mirai.internal.message.refine
 import net.mamoe.mirai.internal.message.toMessageChainOnline
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
@@ -33,7 +34,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
 import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
-import net.mamoe.mirai.message.data.MessageSourceKind
+import net.mamoe.mirai.message.data.MessageSourceKind.GROUP
 import net.mamoe.mirai.utils.*
 
 /**
@@ -119,7 +120,7 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
 
         if (isFromSelfAccount) {
             return GroupMessageSyncEvent(
-                message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP,),
+                message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, GROUP).refine(group),
                 time = msgHead.msgTime,
                 group = group,
                 sender = sender,
@@ -132,7 +133,7 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
             return GroupMessageEvent(
                 senderName = name,
                 sender = sender,
-                message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP),
+                message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, GROUP).refine(group),
                 permission = findMemberPermission(extraInfo?.flags ?: 0, sender, bot),
                 time = msgHead.msgTime
             )