From 91a9e63877fb6f9e8d52d5748d08f647c19f0aee Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 29 Jun 2021 18:05:03 +0800
Subject: [PATCH] Do light refine after constructing `MessageReceipt`, fix
 #1371

---
 .../kotlin/message/MessageReceipt.kt          |  5 ++-
 .../kotlin/contact/NormalMemberImpl.kt        |  8 +++-
 .../kotlin/contact/SendMessageHandler.kt      |  3 +-
 .../commonMain/kotlin/contact/StrangerImpl.kt |  6 ++-
 .../kotlin/message/LongMessageInternal.kt     | 22 +++++++---
 .../kotlin/message/MessageSourceInternal.kt   | 44 +++++++++++++++++--
 .../kotlin/message/outgoingSourceImpl.kt      | 16 +++----
 7 files changed, 83 insertions(+), 21 deletions(-)

diff --git a/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt b/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt
index 626aa3ee1..d7ac48b33 100644
--- a/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt
+++ b/mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt
@@ -40,7 +40,10 @@ import net.mamoe.mirai.utils.MiraiInternalApi
  * @see MessageReceipt.sourceTime 源时间
  */
 @JvmBlockingBridge
-public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor(
+public open class MessageReceipt<out C : Contact>
+@MiraiInternalApi
+@Deprecated("Do not call it directly", level = DeprecationLevel.ERROR)
+constructor(
     /**
      * 指代发送出去的消息.
      */
diff --git a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
index f2742eb17..e1e926055 100644
--- a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
@@ -21,7 +21,10 @@ 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.OnlineMessageSourceToGroupImpl
+import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
 import net.mamoe.mirai.internal.message.OnlineMessageSourceToTempImpl
+import net.mamoe.mirai.internal.message.createMessageReceipt
 import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
@@ -61,7 +64,10 @@ internal class NormalMemberImpl constructor(
     }
 
     private fun MessageReceipt<User>.convert(): MessageReceipt<NormalMemberImpl> {
-        return MessageReceipt(OnlineMessageSourceToTempImpl(source, this@NormalMemberImpl), this@NormalMemberImpl)
+        return OnlineMessageSourceToTempImpl(source, this@NormalMemberImpl).createMessageReceipt(
+            this@NormalMemberImpl,
+            doLightRefine = false //we've already did
+        )
     }
 
 
diff --git a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
index 5cfb40ecc..34d581857 100644
--- a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
@@ -17,6 +17,7 @@ import net.mamoe.mirai.event.nextEventOrNull
 import net.mamoe.mirai.internal.MiraiImpl
 import net.mamoe.mirai.internal.asQQAndroidBot
 import net.mamoe.mirai.internal.message.*
+import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.QQAndroidClient
 import net.mamoe.mirai.internal.network.components.MessageSvcSyncer
@@ -188,7 +189,7 @@ internal abstract class SendMessageHandler<C : Contact> {
                 )
             }
 
-            return MessageReceipt(sourceAwait, contact)
+            return sourceAwait.createMessageReceipt(contact, true)
         }
     }
 
diff --git a/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt b/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
index 3c14f304f..6600d8b56 100644
--- a/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
@@ -28,6 +28,7 @@ import net.mamoe.mirai.event.events.StrangerMessagePostSendEvent
 import net.mamoe.mirai.event.events.StrangerMessagePreSendEvent
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
+import net.mamoe.mirai.internal.message.createMessageReceipt
 import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
@@ -78,7 +79,10 @@ internal class StrangerImpl(
     }
 
     private fun MessageReceipt<User>.convert(): MessageReceipt<StrangerImpl> {
-        return MessageReceipt(OnlineMessageSourceToStrangerImpl(source, this@StrangerImpl), this@StrangerImpl)
+        return OnlineMessageSourceToStrangerImpl(source, this@StrangerImpl).createMessageReceipt(
+            this@StrangerImpl,
+            doLightRefine = false //we've already did
+        )
     }
 
     override fun toString(): String = "Stranger($id)"
diff --git a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
index 54c9fe5e9..1ca40bce2 100644
--- a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
@@ -13,7 +13,9 @@ import net.mamoe.mirai.Bot
 import net.mamoe.mirai.Mirai
 import net.mamoe.mirai.internal.MiraiImpl
 import net.mamoe.mirai.internal.asQQAndroidBot
+import net.mamoe.mirai.internal.getMiraiImpl
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit
+import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.safeCast
 
@@ -43,10 +45,20 @@ internal data class ForwardMessageInternal(
      * not null means nested and need [ForwardMessageInternal.MsgTransmits] in [RefineContext]
      */
     val fileName: String?,
+
+    /**
+     * For light refine before constructing [MessageReceipt].
+     * See [OutgoingMessageSourceInternal] for more details.
+     */
+    val origin: ForwardMessage? = null,
 ) : AbstractServiceMessage(),
     RefinableMessage {
     override val serviceId: Int get() = 35
 
+    override fun tryRefine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message {
+        return origin ?: this
+    }
+
     override suspend fun refine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message {
         bot.asQQAndroidBot()
 
@@ -75,28 +87,28 @@ internal data class ForwardMessageInternal(
             return MessageOrigin(
                 SimpleServiceMessage(serviceId, content),
                 null, // Nested don't have resource id
-                MessageOriginKind.FORWARD
+                MessageOriginKind.FORWARD,
             ) + ForwardMessage(
                 preview = preview,
                 title = title,
                 brief = brief,
                 source = source,
                 summary = summary.trim(),
-                nodeList = MiraiImpl.run { transmits.toForwardMessageNodes(bot, refineContext) }
+                nodeList = getMiraiImpl().run { transmits.toForwardMessageNodes(bot, refineContext) },
             )
         }
 
         return MessageOrigin(
             SimpleServiceMessage(serviceId, content),
             resId,
-            MessageOriginKind.FORWARD
+            MessageOriginKind.FORWARD,
         ) + ForwardMessage(
             preview = preview,
             title = title,
             brief = brief,
             source = source,
             summary = summary.trim(),
-            nodeList = Mirai.downloadForwardMessage(bot, resId!!)
+            nodeList = Mirai.downloadForwardMessage(bot, resId!!),
         )
     }
 
@@ -178,5 +190,5 @@ internal fun RichMessage.Key.forwardMessage(
             <source name="${source.take(50)}" icon="" action="" appid="-1"/>
         </msg>
     """.trimIndent().replace("\n", " ").trim()
-    return ForwardMessageInternal(template, resId, null)
+    return ForwardMessageInternal(template, resId, null, forwardMessage)
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/message/MessageSourceInternal.kt b/mirai-core/src/commonMain/kotlin/message/MessageSourceInternal.kt
index c7edd6b64..c74bbd46f 100644
--- a/mirai-core/src/commonMain/kotlin/message/MessageSourceInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/message/MessageSourceInternal.kt
@@ -10,14 +10,19 @@
 package net.mamoe.mirai.internal.message
 
 import kotlinx.serialization.Transient
+import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.internal.contact.SendMessageHandler
+import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
 import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.message.data.MessageSource
-import net.mamoe.mirai.message.data.sourceOrNull
+import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.utils.cast
 import java.util.concurrent.atomic.AtomicBoolean
 
 
+/**
+ * All [MessageSource] should implement this interface.
+ */
 internal interface MessageSourceInternal {
     @Transient
     val sequenceIds: IntArray // ids
@@ -35,6 +40,37 @@ internal interface MessageSourceInternal {
     fun toJceData(): ImMsgBody.SourceMsg
 }
 
+/**
+ * All [OnlineMessageSource.Outgoing] should implement this interface.
+ */
+internal interface OutgoingMessageSourceInternal : MessageSourceInternal {
+
+    // #1371:
+    // 问题是 `build` 得到的 `ForwardMessage` 会在 `transformSpecialMessages`
+    // 时上传并变成 `ForwardMessageInternal` 再传递给 factory 发送, 并以这个 internal 结果构造了 receipt.
+
+    // 于是构造 receipt 后会进行 light refine 并更新这个属性.
+
+    /**
+     * This 'overrides' [MessageSource.originalMessage].
+     *
+     * @see SendMessageHandler.sendMessagePacket
+     */
+    var originalMessage: MessageChain
+}
+
+@Suppress("DEPRECATION_ERROR")
+internal fun <C : Contact> OnlineMessageSource.Outgoing.createMessageReceipt(
+    target: C,
+    doLightRefine: Boolean,
+): MessageReceipt<C> {
+    if (doLightRefine) {
+        check(this is OutgoingMessageSourceInternal) { "Internal error: source !is OutgoingMessageSourceInternal" }
+        this.originalMessage = this.originalMessage.refineLight(bot)
+    }
+    return MessageReceipt(this, target)
+}
+
 @Suppress("RedundantSuspendModifier", "unused")
 internal suspend fun MessageSource.ensureSequenceIdAvailable() {
     if (this is OnlineMessageSourceToGroupImpl) {
diff --git a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt
index c09f2b1b9..6dc13b627 100644
--- a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt
@@ -79,10 +79,10 @@ internal class OnlineMessageSourceToFriendImpl(
     override val sequenceIds: IntArray,
     override val internalIds: IntArray,
     override val time: Int,
-    override val originalMessage: MessageChain,
+    override var originalMessage: MessageChain,
     override val sender: Bot,
     override val target: Friend,
-) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
+) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal, OutgoingMessageSourceInternal {
     object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
 
     override val bot: Bot
@@ -99,10 +99,10 @@ internal class OnlineMessageSourceToStrangerImpl(
     override val sequenceIds: IntArray,
     override val internalIds: IntArray,
     override val time: Int,
-    override val originalMessage: MessageChain,
+    override var originalMessage: MessageChain,
     override val sender: Bot,
     override val target: Stranger,
-) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal {
+) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal, OutgoingMessageSourceInternal {
 
     constructor(
         delegate: Outgoing,
@@ -125,10 +125,10 @@ internal class OnlineMessageSourceToTempImpl(
     override val sequenceIds: IntArray,
     override val internalIds: IntArray,
     override val time: Int,
-    override val originalMessage: MessageChain,
+    override var originalMessage: MessageChain,
     override val sender: Bot,
     override val target: Member,
-) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal {
+) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal, OutgoingMessageSourceInternal {
     constructor(
         delegate: Outgoing,
         target: Member,
@@ -150,11 +150,11 @@ internal class OnlineMessageSourceToGroupImpl(
     coroutineScope: CoroutineScope,
     override val internalIds: IntArray, // aka random
     override val time: Int,
-    override val originalMessage: MessageChain,
+    override var originalMessage: MessageChain,
     override val sender: Bot,
     override val target: Group,
     providedSequenceIds: IntArray? = null,
-) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal {
+) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal, OutgoingMessageSourceInternal {
     object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
 
     override val ids: IntArray