diff --git a/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt b/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt index c1a3c9a64..6ccac0def 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt @@ -256,7 +256,7 @@ internal open class MultiMsgUploader( fromNick = pm.msg.senderName, msgSeq = pm.seq, msgTime = pm.msg.time, - msgUid = 0x01000000000000000L or pm.uid.toLongUnsigned(), + msgUid = 0x0100000000000000L or pm.uid.toLongUnsigned(), mutiltransHead = MsgComm.MutilTransHead( status = 0, msgId = 1, diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/GeneralMessageSenderProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/GeneralMessageSenderProtocol.kt index fa8aba986..c6872f8c0 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/GeneralMessageSenderProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/GeneralMessageSenderProtocol.kt @@ -27,7 +27,9 @@ import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelin import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.components import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageSender import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer +import net.mamoe.mirai.internal.message.source.OutgoingMessageSourceInternal import net.mamoe.mirai.internal.message.source.createMessageReceipt +import net.mamoe.mirai.internal.network.components.ClockHolder.Companion.clock import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.message.data.AtAll @@ -69,8 +71,15 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S fragmented = step == SendMessageStep.FRAGMENTED || currentMessageChain.contains(ForceAsFragmentedMessage) ) { source = it } - if (sendAllPackets(bot, step, contact, packets)) { + var finalTime = bot.clock.server.currentTimeSeconds().toInt() + val sendPacketOk = sendAllPackets(bot, step, contact, packets) { idx, rsp -> + if (rsp is MessageSvcPbSendMsg.Response.SUCCESS) { + finalTime = rsp.sendTime + } + } + if (sendPacketOk) { val sourceAwait = source?.await() ?: error("Internal error: source is not initialized") + (sourceAwait as OutgoingMessageSourceInternal).time = finalTime sourceAwait.tryEnsureSequenceIdAvailable() collect(sourceAwait.createMessageReceipt(contact, true)) } @@ -83,14 +92,19 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S bot: AbstractBot, step: SendMessageStep, contact: Contact, - packets: List + packets: List, + packetResponseConsumer: (Int, MessageSvcPbSendMsg.Response) -> Unit = { _, _ -> }, ): Boolean { if (!step.allowMultiplePackets && packets.size != 1) { throw IllegalStateException("Internal error: step $step doesn't allow multiple packets while found ${packets.size} ones.") } - packets.forEach { packet -> - if (!sendSinglePacket(bot, packet, step, contact)) return@sendAllPackets false + packets.forEachIndexed { index, packet -> + if (!sendSinglePacket( + bot, packet, step, contact, + index, packetResponseConsumer + ) + ) return@sendAllPackets false } return true @@ -101,12 +115,15 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S packet: OutgoingPacket, step: SendMessageStep, contact: Contact, + index: Int, + packetResponseConsumer: (Int, MessageSvcPbSendMsg.Response) -> Unit, ): Boolean { val originalMessage = attributes[ORIGINAL_MESSAGE] val protocolStrategy = components[MessageProtocolStrategy] val finalMessage = currentMessageChain val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response + packetResponseConsumer(index, resp) if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) { logger.info { "STEP $step: message too large." } val next = step.nextStepOrNull() diff --git a/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt b/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt index 6af3ff68d..1bfdc39dd 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt @@ -61,6 +61,11 @@ internal interface OutgoingMessageSourceInternal : MessageSourceInternal { * This 'overrides' [MessageSource.originalMessage]. */ var originalMessage: MessageChain + + /** + * This for patch outgoing message source to real time (from server) + */ + var time: Int } /** diff --git a/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt index eac8b5edb..bf65d3cf3 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt @@ -48,7 +48,7 @@ internal class OnlineMessageSourceFromFriendImpl( ) : OnlineMessageSource.Incoming.FromFriend(), IncomingMessageSourceInternal { object Serializer : KSerializer by MessageSourceSerializerImpl("OnlineMessageSourceFromFriend") - override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq } + override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq.and(0xFFFF) } private val _isRecalledOrPlanned = atomic(false) @@ -119,10 +119,12 @@ internal class OnlineMessageSourceFromStrangerImpl( private fun List.toJceDataPrivate(ids: IntArray): ImMsgBody.SourceMsg { val elements = flatMap { it.msgBody.richText.elems }.toMutableList().also { - if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2())) + if (it.lastOrNull()?.elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2())) } - first().msgHead.run { + val firstMsgMsgHead = first().msgHead + + firstMsgMsgHead.run { return ImMsgBody.SourceMsg( origSeqs = mapToIntArray { it.msgHead.msgSeq }, senderUin = fromUin, @@ -132,7 +134,7 @@ private fun List.toJceDataPrivate(ids: IntArray): ImMsgBody.SourceM type = 0, time = msgTime, pbReserve = SourceMsg.ResvAttr( - origUids = ids.map { it.toLong() and 0xFFFF_FFFF } + origUids = mutableListOf(firstMsgMsgHead.msgUid) ).toByteArray(SourceMsg.ResvAttr.serializer()), srcMsg = MsgComm.Msg( msgHead = MsgComm.MsgHead( @@ -142,7 +144,8 @@ private fun List.toJceDataPrivate(ids: IntArray): ImMsgBody.SourceM c2cCmd = c2cCmd, msgSeq = msgSeq, msgTime = msgTime, - msgUid = ids.single().toLong() and 0xFFFF_FFFF, // ok + msgUid = firstMsgMsgHead.msgUid, // ok +// msgUid = ids.single().toLong() and 0xFFFF_FFFF, // ok // groupInfo = MsgComm.GroupInfo(groupCode = msgHead.groupInfo.groupCode), isSrcMsg = true ), diff --git a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt index ba05478a0..68d8e2dbe 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt @@ -16,7 +16,6 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl -import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade import net.mamoe.mirai.internal.message.toMessageChainNoSource import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm @@ -27,7 +26,6 @@ import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.message.data.OfflineMessageSource import net.mamoe.mirai.message.data.visitor.MessageVisitor -import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.isSameType import net.mamoe.mirai.utils.mapToIntArray @@ -66,20 +64,9 @@ internal class OfflineMessageSourceImplData( override fun setRecalled(): Boolean = _isRecalledOrPlanned.compareAndSet(expect = false, update = true) override fun toJceData(): ImMsgBody.SourceMsg { - return jceData ?: ImMsgBody.SourceMsg( - origSeqs = sequenceIds, - senderUin = fromId, - toUin = 0, - flag = 1, - elems = originElems ?: MessageProtocolFacade.encode( - originalMessage, messageTarget = null, //forGroup = kind == MessageSourceKind.GROUP, - withGeneralFlags = false - ), - type = 0, - time = time, - pbReserve = EMPTY_BYTE_ARRAY, - srcMsg = EMPTY_BYTE_ARRAY - ).also { jceData = it } + jceData?.let { return it } + + return toJceDataImpl(null).also { jceData = it } } override fun equals(other: Any?): Boolean { diff --git a/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt index 4c789e83d..646ada20a 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt @@ -38,14 +38,15 @@ import net.mamoe.mirai.utils.loadService import net.mamoe.mirai.utils.toLongUnsigned -private fun T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg +internal fun T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg where T : MessageSourceInternal, T : MessageSource { - val elements = MessageProtocolFacade.encode(originalMessage, subject, withGeneralFlags = true) + val elements = MessageProtocolFacade.encode(originalMessage, subject, withGeneralFlags = false) val pdReserve = SourceMsg.ResvAttr( - origUids = sequenceIds.zip(internalIds) - .map { (seq, internal) -> seq.toLong().shl(32) or internal.toLongUnsigned() } + origUids = internalIds.map { 0x100000000000000 or it.toLongUnsigned() } +// origUids = sequenceIds.zip(internalIds) +// .map { (seq, internal) -> seq.toLong().shl(32) or internal.toLongUnsigned() } ) return ImMsgBody.SourceMsg( @@ -85,7 +86,7 @@ private fun T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg internal class OnlineMessageSourceToFriendImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, - override val time: Int, + override var time: Int, override var originalMessage: MessageChain, override val sender: Bot, override val target: Friend, @@ -119,7 +120,7 @@ internal class OnlineMessageSourceToFriendImpl( internal class OnlineMessageSourceToStrangerImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, - override val time: Int, + override var time: Int, override var originalMessage: MessageChain, override val sender: Bot, override val target: Stranger, @@ -158,7 +159,7 @@ internal class OnlineMessageSourceToStrangerImpl( internal class OnlineMessageSourceToTempImpl( override val sequenceIds: IntArray, override val internalIds: IntArray, - override val time: Int, + override var time: Int, override var originalMessage: MessageChain, override val sender: Bot, override val target: Member, @@ -197,7 +198,7 @@ internal class OnlineMessageSourceToTempImpl( internal class OnlineMessageSourceToGroupImpl( coroutineScope: CoroutineScope, override val internalIds: IntArray, // aka random - override val time: Int, + override var time: Int, override var originalMessage: MessageChain, override val sender: Bot, override val target: Group, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt index f07f0fc76..aa2655e2d 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt @@ -49,8 +49,10 @@ import kotlin.random.Random internal object MessageSvcPbSendMsg : OutgoingPacketFactory("MessageSvc.PbSendMsg") { sealed class Response : Packet { - object SUCCESS : Response() { - override fun toString(): String = "MessageSvcPbSendMsg.Response.SUCCESS" + class SUCCESS( + val sendTime: Int, + ) : Response() { + override fun toString(): String = "MessageSvcPbSendMsg.Response.SUCCESS(time=$sendTime)" } object MessageTooLarge : Response() { @@ -486,7 +488,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Response.SUCCESS + 0 -> Response.SUCCESS(response.sendTime) 10 -> Response.MessageTooLarge 32 -> Response.ServiceUnavailable else -> Response.Failed( diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt index 0c2216869..1d2c10ef8 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt @@ -252,7 +252,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler open inner class TestMessageProtocolStrategy : MessageProtocolStrategy { override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet { assertEquals(0x123, packet.sequenceId) - return MessageSvcPbSendMsg.Response.SUCCESS + return MessageSvcPbSendMsg.Response.SUCCESS(123) } override suspend fun createPacketsForGeneralMessage( diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt index 3020ef868..ee6e2bc88 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt @@ -21,7 +21,6 @@ import net.mamoe.mirai.internal.testFramework.runDynamicTests import net.mamoe.mirai.internal.utils.runCoroutineInPlace import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.MessageSource.Key.quote -import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.hexToBytes import kotlin.test.Test @@ -90,8 +89,9 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() { ), ), ), - srcMsg = EMPTY_BYTE_ARRAY - // mirai's OfflineMessageSource has no enough information to create 'srcMsg' + pbReserve = "18 AE FB A2 F7 86 80 80 80 01".hexToBytes(), + srcMsg = "0A 2C 08 B1 89 4B 10 DD F1 92 B7 07 18 09 20 0B 28 E7 8B FE FF FF FF FF FF FF 01 30 B2 85 AF 94 06 38 AE FB A2 F7 86 80 80 80 01 E0 01 01 1A 0D 0A 0B 12 05 0A 03 0A 01 61 12 02 4A 00".hexToBytes(), + toUin = 1994701021, ), ), net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( @@ -192,8 +192,8 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() { ), ), ), - pbReserve = "18 BA 92 F1 A4 02".hexToBytes(), - srcMsg = "0A 20 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 8D F4 01 30 B0 A7 AF 94 06 38 BA 92 F1 A4 02 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 61 12 02 4A 00".hexToBytes(), + pbReserve = "18 BA 92 F1 A4 82 80 80 80 01".hexToBytes(), + srcMsg = "0A 24 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 8D F4 01 30 B0 A7 AF 94 06 38 BA 92 F1 A4 82 80 80 80 01 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 61 12 02 4A 00".hexToBytes(), toUin = 1230002, ), ), @@ -330,8 +330,9 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() { ), ), ), - srcMsg = EMPTY_BYTE_ARRAY - // mirai's OfflineMessageSource has no enough information to create 'srcMsg' + pbReserve = "18 AE FB A2 F7 86 80 80 80 01".hexToBytes(), + srcMsg = "0A 2C 08 B1 89 4B 10 DD F1 92 B7 07 18 09 20 0B 28 E7 8B FE FF FF FF FF FF FF 01 30 B2 85 AF 94 06 38 AE FB A2 F7 86 80 80 80 01 E0 01 01 1A 0D 0A 0B 12 05 0A 03 0A 01 61 12 02 4A 00".hexToBytes(), + toUin = 1994701021, ), ), net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( @@ -430,9 +431,8 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() { ), ), ), - pbReserve = "18 BD F9 EF D7 06".hexToBytes(), - // srcMsg is available for online source - srcMsg = "0A 20 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 F6 F3 01 30 83 91 AF 94 06 38 BD F9 EF D7 06 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 4F 12 02 4A 00".hexToBytes(), + pbReserve = "18 BD F9 EF D7 86 80 80 80 01".hexToBytes(), + srcMsg = "0A 24 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 F6 F3 01 30 83 91 AF 94 06 38 BD F9 EF D7 86 80 80 80 01 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 4F 12 02 4A 00".hexToBytes(), toUin = 1230002, ), ),