Add LongMessageProtocolTest and various improvements:

Change attributes carrying helper objects to components

Make ClockHolder open

Use originalMessage for MessageReceipt
This commit is contained in:
Him188 2022-05-23 13:29:13 +01:00
parent 3b7eb40529
commit c8fb354d13
22 changed files with 438 additions and 159 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2021 Mamoe Technologies and contributors. * Copyright 2019-2022 Mamoe Technologies and contributors.
* *
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -11,7 +11,7 @@ package net.mamoe.mirai.utils
public interface Clock { public interface Clock {
public fun currentTimeMillis(): Long public fun currentTimeMillis(): Long
public fun currentTimeSeconds(): Long public fun currentTimeSeconds(): Long = currentTimeMillis() / 1000
public object SystemDefault : Clock { public object SystemDefault : Clock {
override fun currentTimeMillis(): Long = net.mamoe.mirai.utils.currentTimeMillis() override fun currentTimeMillis(): Long = net.mamoe.mirai.utils.currentTimeMillis()

View File

@ -26,6 +26,7 @@ import net.mamoe.mirai.internal.message.protocol.outgoing.HighwayUploader
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.network.component.buildComponentStorage import net.mamoe.mirai.internal.network.component.buildComponentStorage
import net.mamoe.mirai.internal.network.components.BdhSession import net.mamoe.mirai.internal.network.components.BdhSession
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.internal.network.highway.ChannelKind import net.mamoe.mirai.internal.network.highway.ChannelKind
import net.mamoe.mirai.internal.network.highway.Highway 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.ResourceKind.PRIVATE_IMAGE
@ -261,6 +262,7 @@ internal suspend fun <C : AbstractContact> C.sendMessageImpl(
MessageProtocolFacade.preprocessAndSendOutgoing(this, message, buildComponentStorage { MessageProtocolFacade.preprocessAndSendOutgoing(this, message, buildComponentStorage {
set(MessageProtocolStrategy, messageProtocolStrategy) set(MessageProtocolStrategy, messageProtocolStrategy)
set(HighwayUploader, HighwayUploader.Default) set(HighwayUploader, HighwayUploader.Default)
set(ClockHolder, bot.components[ClockHolder])
}) })
} }

View File

@ -45,16 +45,27 @@ internal suspend fun <C : Contact> C.broadcastMessagePreSendEvent(
internal enum class SendMessageStep { internal enum class SendMessageStep {
/**
* 尝试单包直接发送全部消息
*/
FIRST { FIRST {
override fun nextStepOrNull(): SendMessageStep { override fun nextStepOrNull(): SendMessageStep {
return LONG_MESSAGE return LONG_MESSAGE
} }
}, },
/**
* 尝试通过长消息通道上传长消息取得 resId 后再通过普通消息通道发送长消息标识
*/
LONG_MESSAGE { LONG_MESSAGE {
override fun nextStepOrNull(): SendMessageStep { override fun nextStepOrNull(): SendMessageStep {
return FRAGMENTED return FRAGMENTED
} }
}, },
/**
* 发送分片多包发送
*/
FRAGMENTED { FRAGMENTED {
override fun nextStepOrNull(): SendMessageStep? { override fun nextStepOrNull(): SendMessageStep? {
return null return null

View File

@ -152,34 +152,6 @@ internal data class ForwardMessageInternal(
} }
} }
internal fun RichMessage.Key.longMessage(brief: String, resId: String, timeSeconds: Long): LongMessageInternal {
val limited: String = if (brief.length > 30) {
brief.take(30) + ""
} else {
brief
}
val template = """
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<msg serviceID="35" templateID="1" action="viewMultiMsg"
brief="$limited"
m_resid="$resId"
m_fileName="$timeSeconds" sourceMsgId="0" url=""
flag="3" adverSign="0" multiMsgFlag="1">
<item layout="1">
<title>$limited</title>
<hr hidden="false" style="0"/>
<summary>点击查看完整消息</summary>
</item>
<source name="聊天记录" icon="" action="" appid="-1"/>
</msg>
""".trimIndent().trim()
return LongMessageInternal(template, resId)
}
private fun String.xmlEnc(): String { private fun String.xmlEnc(): String {
return this.replace("&", "&amp;") return this.replace("&", "&amp;")
} }

View File

@ -17,11 +17,9 @@ import net.mamoe.mirai.internal.contact.impl
import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.contact.userIdOrNull import net.mamoe.mirai.internal.contact.userIdOrNull
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
import net.mamoe.mirai.internal.message.protocol.outgoing.HighwayUploader
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.message.source.MessageSourceInternal import net.mamoe.mirai.internal.message.source.MessageSourceInternal
import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.component.buildComponentStorage import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.highway.Highway import net.mamoe.mirai.internal.network.highway.Highway
import net.mamoe.mirai.internal.network.highway.ResourceKind import net.mamoe.mirai.internal.network.highway.ResourceKind
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
@ -42,21 +40,19 @@ import kotlin.random.Random
internal open class MultiMsgUploader( internal open class MultiMsgUploader(
val client: QQAndroidClient, val client: QQAndroidClient,
val isLong: Boolean, val isLong: Boolean,
val tmpRand: Random = Random.Default, val random: Random,
val facade: MessageProtocolFacade,
val contact: Contact, val contact: Contact,
val strategy: MessageProtocolStrategy<*>, val components: ComponentStorage,
val senderName: String val senderName: String
) { ) {
protected open fun newUploader(): MultiMsgUploader = MultiMsgUploader( protected open fun newUploader(): MultiMsgUploader = MultiMsgUploader(
isLong = isLong,
client = client, client = client,
tmpRand = tmpRand, isLong = isLong,
facade = facade, random = random,
contact = contact, contact = contact,
senderName = senderName, components = components,
strategy = strategy senderName = senderName
) )
val mainMsg = mutableListOf<MsgComm.Msg>() val mainMsg = mutableListOf<MsgComm.Msg>()
@ -69,7 +65,7 @@ internal open class MultiMsgUploader(
protected open fun newNid(): String { protected open fun newNid(): String {
var nid: String var nid: String
do { do {
nid = "${tmpRand.nextInt().absoluteValue}" nid = "${random.nextInt().absoluteValue}"
} while (nestedMsgs.containsKey(nid)) } while (nestedMsgs.containsKey(nid))
return nid return nid
} }
@ -140,10 +136,7 @@ internal open class MultiMsgUploader(
msgChain = convertNestedForwardMessage(nestedForward, msgChain) msgChain = convertNestedForwardMessage(nestedForward, msgChain)
} }
msgChain = facade.preprocess(contact.impl(), msgChain, buildComponentStorage { msgChain = components[MessageProtocolFacade].preprocess(contact.impl(), msgChain, components)
set(MessageProtocolStrategy, strategy)
set(HighwayUploader, HighwayUploader.Default)
})
var seq: Int = -1 var seq: Int = -1
var uid: Int = -1 var uid: Int = -1
@ -157,8 +150,8 @@ internal open class MultiMsgUploader(
if (seq != -1 && uid != -1) { if (seq != -1 && uid != -1) {
if (existsIds.add(seq.concatAsLong(uid))) break if (existsIds.add(seq.concatAsLong(uid))) break
} }
seq = tmpRand.nextInt().absoluteValue seq = random.nextInt().absoluteValue
uid = tmpRand.nextInt().absoluteValue uid = random.nextInt().absoluteValue
} }
val msg0 = MsgComm.Msg( val msg0 = MsgComm.Msg(
@ -176,18 +169,13 @@ internal open class MultiMsgUploader(
), ),
msgType = 82, // troop, msgType = 82, // troop,
groupInfo = if (contact is Group) MsgComm.GroupInfo( groupInfo = if (contact is Group) MsgComm.GroupInfo(
groupCode = contact.groupCode, groupCode = contact.groupCode, groupCard = senderName // Cinnamon
groupCard = senderName // Cinnamon
) else null, ) else null,
isSrcMsg = false, isSrcMsg = false,
), ), msgBody = ImMsgBody.MsgBody(
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText( richText = ImMsgBody.RichText(
elems = MessageProtocolFacade.encode( elems = MessageProtocolFacade.encode(
msgChain, msgChain, messageTarget = contact, withGeneralFlags = false, isForward = true
messageTarget = contact,
withGeneralFlags = false,
isForward = true
) )
) )
) )
@ -197,17 +185,13 @@ internal open class MultiMsgUploader(
} }
open fun toMessageValidationData(): MessageValidationData { open fun toMessageValidationData(): MessageValidationData {
val msgTransmit = MsgTransmit.PbMultiMsgTransmit( val msgTransmit =
msg = mainMsg, MsgTransmit.PbMultiMsgTransmit(msg = mainMsg, pbItemList = nestedMsgs.asSequence().map { (name, msgList) ->
pbItemList = nestedMsgs.asSequence() MsgTransmit.PbMultiMsgItem(
.map { (name, msgList) -> fileName = name,
MsgTransmit.PbMultiMsgItem( buffer = MsgTransmit.PbMultiMsgNew(msgList).toByteArray(MsgTransmit.PbMultiMsgNew.serializer())
fileName = name, )
buffer = MsgTransmit.PbMultiMsgNew(msgList).toByteArray(MsgTransmit.PbMultiMsgNew.serializer()) }.toList())
)
}
.toList()
)
val bytes = msgTransmit.toByteArray(MsgTransmit.PbMultiMsgTransmit.serializer()) val bytes = msgTransmit.toByteArray(MsgTransmit.PbMultiMsgTransmit.serializer())
return MessageValidationData(bytes.gzip()) return MessageValidationData(bytes.gzip())
@ -218,27 +202,20 @@ internal open class MultiMsgUploader(
val response = client.bot.network.sendAndExpect( val response = client.bot.network.sendAndExpect(
MultiMsg.ApplyUp.createForGroup( MultiMsg.ApplyUp.createForGroup(
buType = if (isLong) 1 else 2, buType = if (isLong) 1 else 2, client = client, messageData = data, dstUin = contact.uin
client = client,
messageData = data,
dstUin = contact.uin
) )
) )
lateinit var resId: String lateinit var resId: String
when (response) { when (response) {
is MultiMsg.ApplyUp.Response.MessageTooLarge -> is MultiMsg.ApplyUp.Response.MessageTooLarge -> error(
error( "Internal error: message is too large, but this should be handled before sending. "
"Internal error: message is too large, but this should be handled before sending. " )
)
is MultiMsg.ApplyUp.Response.RequireUpload -> { is MultiMsg.ApplyUp.Response.RequireUpload -> {
resId = response.proto.msgResid resId = response.proto.msgResid
val body = LongMsg.ReqBody( val body = LongMsg.ReqBody(
subcmd = 1, subcmd = 1, platformType = 9, termType = 5, msgUpReq = listOf(
platformType = 9,
termType = 5,
msgUpReq = listOf(
LongMsg.MsgUpReq( LongMsg.MsgUpReq(
msgType = 3, // group msgType = 3, // group
dstUin = contact.uin, dstUin = contact.uin,
@ -253,14 +230,10 @@ internal open class MultiMsgUploader(
body.toExternalResource().use { resource -> body.toExternalResource().use { resource ->
Highway.uploadResourceBdh( Highway.uploadResourceBdh(
bot = client.bot, bot = client.bot, resource = resource, kind = when (isLong) {
resource = resource,
kind = when (isLong) {
true -> ResourceKind.LONG_MESSAGE true -> ResourceKind.LONG_MESSAGE
false -> ResourceKind.FORWARD_MESSAGE false -> ResourceKind.FORWARD_MESSAGE
}, }, commandId = 27, initialTicket = response.proto.msgSig
commandId = 27,
initialTicket = response.proto.msgSig
) )
} }
} }

View File

@ -22,7 +22,10 @@ import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.message.protocol.decode.* import net.mamoe.mirai.internal.message.protocol.decode.*
import net.mamoe.mirai.internal.message.protocol.encode.* import net.mamoe.mirai.internal.message.protocol.encode.*
import net.mamoe.mirai.internal.message.protocol.outgoing.* import net.mamoe.mirai.internal.message.protocol.outgoing.*
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.component.buildComponentStorage
import net.mamoe.mirai.internal.network.component.withFallback
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.pipeline.ProcessResult import net.mamoe.mirai.internal.pipeline.ProcessResult
import net.mamoe.mirai.internal.utils.runCoroutineInPlace import net.mamoe.mirai.internal.utils.runCoroutineInPlace
@ -39,6 +42,8 @@ import java.util.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal interface MessageProtocolFacade { internal interface MessageProtocolFacade {
val remark: String get() = "MessageProtocolFacade"
val encoderPipeline: MessageEncoderPipeline val encoderPipeline: MessageEncoderPipeline
val decoderPipeline: MessageDecoderPipeline val decoderPipeline: MessageDecoderPipeline
val preprocessorPipeline: OutgoingMessagePipeline val preprocessorPipeline: OutgoingMessagePipeline
@ -129,7 +134,8 @@ internal interface MessageProtocolFacade {
/** /**
* The default global instance. * The default global instance.
*/ */
companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl() companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl(),
ComponentKey<MessageProtocolFacade>
} }
internal fun MessageProtocolFacade.decodeAndRefineLight( internal fun MessageProtocolFacade.decodeAndRefineLight(
@ -150,7 +156,8 @@ internal suspend fun MessageProtocolFacade.decodeAndRefineDeep(
internal class MessageProtocolFacadeImpl( internal class MessageProtocolFacadeImpl(
private val protocols: Iterable<MessageProtocol> = ServiceLoader.load(MessageProtocol::class.java) private val protocols: Iterable<MessageProtocol> = ServiceLoader.load(MessageProtocol::class.java),
override val remark: String = "MessageProtocolFacade"
) : MessageProtocolFacade { ) : MessageProtocolFacade {
override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl() override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl()
override val decoderPipeline: MessageDecoderPipeline = MessageDecoderPipelineImpl() override val decoderPipeline: MessageDecoderPipeline = MessageDecoderPipelineImpl()
@ -243,6 +250,15 @@ internal class MessageProtocolFacadeImpl(
} }
} }
private val thisComponentStorage by lazy {
buildComponentStorage {
set(
MessageProtocolFacade,
this@MessageProtocolFacadeImpl
)
}
}
override suspend fun <C : AbstractContact> preprocess( override suspend fun <C : AbstractContact> preprocess(
target: C, target: C,
message: Message, message: Message,
@ -314,9 +330,9 @@ internal class MessageProtocolFacadeImpl(
val attributes = buildTypeSafeMap { val attributes = buildTypeSafeMap {
set(OutgoingMessagePipelineContext.CONTACT, target.impl()) set(OutgoingMessagePipelineContext.CONTACT, target.impl())
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE, message) set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE, message)
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE_AS_CHAIN, message.toMessageChain())
set(OutgoingMessagePipelineContext.STEP, SendMessageStep.FIRST) set(OutgoingMessagePipelineContext.STEP, SendMessageStep.FIRST)
set(OutgoingMessagePipelineContext.PROTOCOL_STRATEGY, context[MessageProtocolStrategy].castUp()) set(OutgoingMessagePipelineContext.COMPONENTS, thisComponentStorage.withFallback(context))
set(OutgoingMessagePipelineContext.HIGHWAY_UPLOADER, context[HighwayUploader])
} }
return attributes return attributes
} }

View File

@ -19,9 +19,11 @@ import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoder import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoder
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderContext import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderContext
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.PROTOCOL_STRATEGY import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
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.outgoing.OutgoingMessageSender
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
import net.mamoe.mirai.internal.message.source.createMessageReceipt import net.mamoe.mirai.internal.message.source.createMessageReceipt
@ -81,11 +83,11 @@ internal class FileMessageProtocol : MessageProtocol() {
val contact = attributes[CONTACT] val contact = attributes[CONTACT]
val bot = contact.bot val bot = contact.bot
val strategy = attributes[PROTOCOL_STRATEGY] val strategy = components[MessageProtocolStrategy]
val source = coroutineScope { val source = coroutineScope {
val source = async { val source = async {
strategy.constructSourceForSpecialMessage(currentMessageChain, 2021) strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 2021)
} }
bot.network.sendAndExpect(FileManagement.Feed(bot.client, contact.id, file.busId, file.id)) bot.network.sendAndExpect(FileManagement.Feed(bot.client, contact.id, file.busId, file.id))

View File

@ -14,15 +14,15 @@ import net.mamoe.mirai.internal.message.data.forwardMessage
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
import net.mamoe.mirai.internal.message.protocol.outgoing.HighwayUploader
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.HIGHWAY_UPLOADER import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.components
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.PROTOCOL_STRATEGY
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePreprocessor import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePreprocessor
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.message.data.ForwardMessage import net.mamoe.mirai.message.data.ForwardMessage
import net.mamoe.mirai.message.data.RichMessage import net.mamoe.mirai.message.data.RichMessage
import net.mamoe.mirai.message.data.toMessageChain import net.mamoe.mirai.message.data.toMessageChain
import net.mamoe.mirai.utils.currentTimeSeconds
internal class ForwardMessageProtocol : MessageProtocol() { internal class ForwardMessageProtocol : MessageProtocol() {
override fun ProcessorCollector.collectProcessorsImpl() { override fun ProcessorCollector.collectProcessorsImpl() {
@ -46,16 +46,16 @@ internal class ForwardMessageProtocol : MessageProtocol() {
}.asIterable().verifyLength(forward, contact) }.asIterable().verifyLength(forward, contact)
} }
val resId = attributes[HIGHWAY_UPLOADER].uploadMessages( val resId = components[HighwayUploader].uploadMessages(
contact, contact,
attributes[PROTOCOL_STRATEGY], components,
forward.nodeList, forward.nodeList,
false false
) )
currentMessageChain = RichMessage.forwardMessage( currentMessageChain = RichMessage.forwardMessage(
resId = resId, resId = resId,
fileName = currentTimeSeconds().toString(), fileName = components[ClockHolder].local.currentTimeSeconds().toString(),
forwardMessage = forward, forwardMessage = forward,
).toMessageChain() ).toMessageChain()
} }

View File

@ -12,14 +12,17 @@ package net.mamoe.mirai.internal.message.protocol.impl
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.internal.AbstractBot import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.SendMessageStep import net.mamoe.mirai.internal.contact.SendMessageStep
import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.PROTOCOL_STRATEGY import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP
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.outgoing.OutgoingMessageSender
import net.mamoe.mirai.internal.message.source.createMessageReceipt import net.mamoe.mirai.internal.message.source.createMessageReceipt
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
@ -41,7 +44,8 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
override suspend fun OutgoingMessagePipelineContext.process() { override suspend fun OutgoingMessagePipelineContext.process() {
markAsConsumed() markAsConsumed()
val strategy = attributes[PROTOCOL_STRATEGY] @Suppress("UNCHECKED_CAST")
val strategy = components[MessageProtocolStrategy] as MessageProtocolStrategy<AbstractContact>
val step = attributes[STEP] val step = attributes[STEP]
val contact = attributes[CONTACT] val contact = attributes[CONTACT]
val bot = contact.bot val bot = contact.bot
@ -52,9 +56,9 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
client = bot.client, client = bot.client,
contact = contact, contact = contact,
message = currentMessageChain, message = currentMessageChain,
fragmented = step == SendMessageStep.FRAGMENTED, originalMessage = attributes[ORIGINAL_MESSAGE_AS_CHAIN],
sourceCallback = { source = it } fragmented = step == SendMessageStep.FRAGMENTED
) ) { source = it }
sendAllPackets(bot, step, contact, packets) sendAllPackets(bot, step, contact, packets)
@ -70,7 +74,7 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
packets: List<OutgoingPacket> packets: List<OutgoingPacket>
) = packets.forEach { packet -> ) = packets.forEach { packet ->
val originalMessage = attributes[ORIGINAL_MESSAGE] val originalMessage = attributes[ORIGINAL_MESSAGE]
val protocolStrategy = attributes[PROTOCOL_STRATEGY] val protocolStrategy = components[MessageProtocolStrategy]
val finalMessage = currentMessageChain val finalMessage = currentMessageChain
val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response

View File

@ -12,22 +12,21 @@ package net.mamoe.mirai.internal.message.protocol.impl
import net.mamoe.mirai.internal.contact.AbstractContact import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.SendMessageStep import net.mamoe.mirai.internal.contact.SendMessageStep
import net.mamoe.mirai.internal.contact.takeContent import net.mamoe.mirai.internal.contact.takeContent
import net.mamoe.mirai.internal.message.data.longMessage import net.mamoe.mirai.internal.message.data.LongMessageInternal
import net.mamoe.mirai.internal.message.flags.DontAsLongMessage import net.mamoe.mirai.internal.message.flags.DontAsLongMessage
import net.mamoe.mirai.internal.message.flags.ForceAsLongMessage import net.mamoe.mirai.internal.message.flags.ForceAsLongMessage
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy import net.mamoe.mirai.internal.message.protocol.outgoing.HighwayUploader
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.HIGHWAY_UPLOADER
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.PROTOCOL_STRATEGY
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.components
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.RichMessage import net.mamoe.mirai.utils.truncated
import net.mamoe.mirai.utils.currentTimeSeconds
internal class LongMessageProtocol : MessageProtocol() { internal class LongMessageProtocol : MessageProtocol() {
override fun ProcessorCollector.collectProcessorsImpl() { override fun ProcessorCollector.collectProcessorsImpl() {
@ -36,24 +35,25 @@ internal class LongMessageProtocol : MessageProtocol() {
convertToLongMessageIfNeeded( convertToLongMessageIfNeeded(
currentMessageChain, currentMessageChain,
attributes[STEP], attributes[STEP],
attributes[CONTACT], attributes[CONTACT]
attributes[PROTOCOL_STRATEGY]
) )
}) })
} }
/**
* Convert to [LongMessageInternal] iff [SendMessageStep.FIRST] has failed.
*/
private suspend fun OutgoingMessagePipelineContext.convertToLongMessageIfNeeded( private suspend fun OutgoingMessagePipelineContext.convertToLongMessageIfNeeded(
chain: MessageChain, chain: MessageChain,
step: SendMessageStep, step: SendMessageStep,
contact: AbstractContact, contact: AbstractContact,
strategy: MessageProtocolStrategy<*>
): MessageChain { ): MessageChain {
val uploader = attributes[HIGHWAY_UPLOADER] val uploader = components[HighwayUploader]
suspend fun sendLongImpl(): MessageChain { suspend fun sendLongImpl(): MessageChain {
val time = currentTimeSeconds() val time = components[ClockHolder].local.currentTimeSeconds()
val resId = uploader.uploadLongMessage(contact, strategy, chain, time.toInt()) val resId = uploader.uploadLongMessage(contact, components, chain, time.toInt())
return chain + RichMessage.longMessage( return chain + createLongMessage(
brief = chain.takeContent(27), brief = chain.takeContent(27),
resId = resId, resId = resId,
timeSeconds = time timeSeconds = time
@ -81,4 +81,26 @@ internal class LongMessageProtocol : MessageProtocol() {
SendMessageStep.FRAGMENTED -> chain SendMessageStep.FRAGMENTED -> chain
} }
} }
private fun createLongMessage(brief: String, resId: String, timeSeconds: Long): LongMessageInternal {
val limited: String = brief.truncated(30)
val template = """
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<msg serviceID="35" templateID="1" action="viewMultiMsg"
brief="$limited"
m_resid="$resId"
m_fileName="$timeSeconds" sourceMsgId="0" url=""
flag="3" adverSign="0" multiMsgFlag="1">
<item layout="1">
<title>$limited</title>
<hr hidden="false" style="0"/>
<summary>点击查看完整消息</summary>
</item>
<source name="聊天记录" icon="" action="" appid="-1"/>
</msg>
""".trimIndent().trim()
return LongMessageInternal(template, resId)
}
} }

View File

@ -14,8 +14,11 @@ import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoder import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoder
import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderContext import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderContext
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
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.outgoing.OutgoingMessageSender
import net.mamoe.mirai.internal.message.source.createMessageReceipt import net.mamoe.mirai.internal.message.source.createMessageReceipt
import net.mamoe.mirai.internal.network.protocol.packet.chat.MusicSharePacket import net.mamoe.mirai.internal.network.protocol.packet.chat.MusicSharePacket
@ -55,8 +58,8 @@ internal class MusicShareProtocol : MessageProtocol() {
val result = bot.network.sendAndExpect(packet) val result = bot.network.sendAndExpect(packet)
result.pkg.checkSuccess("send music share") result.pkg.checkSuccess("send music share")
val strategy = attributes[OutgoingMessagePipelineContext.PROTOCOL_STRATEGY] val strategy = components[MessageProtocolStrategy]
val source = strategy.constructSourceForSpecialMessage(currentMessageChain, 3116) val source = strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 3116)
source.tryEnsureSequenceIdAvailable() source.tryEnsureSequenceIdAvailable()
collect(source.createMessageReceipt(contact, true)) collect(source.createMessageReceipt(contact, true))

View File

@ -12,19 +12,20 @@ package net.mamoe.mirai.internal.message.protocol.outgoing
import net.mamoe.mirai.internal.contact.AbstractContact import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.nickIn import net.mamoe.mirai.internal.contact.nickIn
import net.mamoe.mirai.internal.message.data.MultiMsgUploader import net.mamoe.mirai.internal.message.data.MultiMsgUploader
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
import net.mamoe.mirai.internal.message.source.ensureSequenceIdAvailable import net.mamoe.mirai.internal.message.source.ensureSequenceIdAvailable
import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.message.data.ForwardMessage import net.mamoe.mirai.message.data.ForwardMessage
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import kotlin.random.Random
internal interface HighwayUploader { internal interface HighwayUploader {
suspend fun uploadMessages( suspend fun uploadMessages(
contact: AbstractContact, contact: AbstractContact,
strategy: MessageProtocolStrategy<*>, components: ComponentStorage,
nodes: Collection<ForwardMessage.INode>, nodes: Collection<ForwardMessage.INode>,
isLong: Boolean, isLong: Boolean,
facade: MessageProtocolFacade = MessageProtocolFacade,
senderName: String = contact.bot.nickIn(contact), senderName: String = contact.bot.nickIn(contact),
): String { ): String {
nodes.forEach { it.messageChain.ensureSequenceIdAvailable() } nodes.forEach { it.messageChain.ensureSequenceIdAvailable() }
@ -32,10 +33,10 @@ internal interface HighwayUploader {
val uploader = MultiMsgUploader( val uploader = MultiMsgUploader(
client = contact.bot.client, client = contact.bot.client,
isLong = isLong, isLong = isLong,
facade = facade,
contact = contact, contact = contact,
random = Random(components[ClockHolder].local.currentTimeSeconds()),
senderName = senderName, senderName = senderName,
strategy = strategy components = components
).also { it.emitMain(nodes) } ).also { it.emitMain(nodes) }
return uploader.uploadAndReturnResId() return uploader.uploadAndReturnResId()
@ -43,7 +44,7 @@ internal interface HighwayUploader {
suspend fun uploadLongMessage( suspend fun uploadLongMessage(
contact: AbstractContact, contact: AbstractContact,
strategy: MessageProtocolStrategy<*>, components: ComponentStorage,
chain: MessageChain, chain: MessageChain,
timeSeconds: Int, timeSeconds: Int,
senderName: String = contact.bot.nickIn(contact), senderName: String = contact.bot.nickIn(contact),
@ -51,7 +52,7 @@ internal interface HighwayUploader {
val bot = contact.bot val bot = contact.bot
return uploadMessages( return uploadMessages(
contact, contact,
strategy, components,
listOf( listOf(
ForwardMessage.Node( ForwardMessage.Node(
senderId = bot.id, senderId = bot.id,
@ -61,7 +62,7 @@ internal interface HighwayUploader {
) )
), ),
true, true,
senderName = senderName senderName = senderName,
) )
} }

View File

@ -37,13 +37,14 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
suspend fun createPacketsForGeneralMessage( suspend fun createPacketsForGeneralMessage(
client: QQAndroidClient, client: QQAndroidClient,
contact: C, contact: C,
message: MessageChain, message: MessageChain, // to send
originalMessage: MessageChain, // to create Receipt
fragmented: Boolean, fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit, sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit,
): List<OutgoingPacket> ): List<OutgoingPacket>
suspend fun constructSourceForSpecialMessage( suspend fun constructSourceForSpecialMessage(
finalMessage: MessageChain, originalMessage: MessageChain,
fromAppId: Int, fromAppId: Int,
): OnlineMessageSource.Outgoing ): OnlineMessageSource.Outgoing
@ -52,7 +53,7 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
internal sealed class UserMessageProtocolStrategy<C : AbstractUser> : MessageProtocolStrategy<C> { internal sealed class UserMessageProtocolStrategy<C : AbstractUser> : MessageProtocolStrategy<C> {
override suspend fun constructSourceForSpecialMessage( override suspend fun constructSourceForSpecialMessage(
finalMessage: MessageChain, originalMessage: MessageChain,
fromAppId: Int fromAppId: Int
): OnlineMessageSource.Outgoing { ): OnlineMessageSource.Outgoing {
throw UnsupportedOperationException("Sending MusicShare or FileMessage to User is not yet supported") throw UnsupportedOperationException("Sending MusicShare or FileMessage to User is not yet supported")
@ -66,14 +67,15 @@ internal class FriendMessageProtocolStrategy(
client: QQAndroidClient, client: QQAndroidClient,
contact: FriendImpl, contact: FriendImpl,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
): List<OutgoingPacket> { ): List<OutgoingPacket> {
return MessageSvcPbSendMsg.createToFriend(client, contact, message, fragmented, sourceCallback) return MessageSvcPbSendMsg.createToFriend(client, contact, message, originalMessage, fragmented, sourceCallback)
} }
override suspend fun constructSourceForSpecialMessage( override suspend fun constructSourceForSpecialMessage(
finalMessage: MessageChain, originalMessage: MessageChain,
fromAppId: Int fromAppId: Int
): OnlineMessageSource.Outgoing { ): OnlineMessageSource.Outgoing {
val receipt: PrivateMessageProcessor.SendPrivateMessageReceipt = withTimeoutOrNull(3000) { val receipt: PrivateMessageProcessor.SendPrivateMessageReceipt = withTimeoutOrNull(3000) {
@ -88,7 +90,7 @@ internal class FriendMessageProtocolStrategy(
sender = contact.bot, sender = contact.bot,
target = contact, target = contact,
time = contact.bot.clock.server.currentTimeSeconds().toInt(), time = contact.bot.clock.server.currentTimeSeconds().toInt(),
originalMessage = finalMessage originalMessage = originalMessage
) )
} }
} }
@ -98,10 +100,18 @@ internal object StrangerMessageProtocolStrategy : UserMessageProtocolStrategy<St
client: QQAndroidClient, client: QQAndroidClient,
contact: StrangerImpl, contact: StrangerImpl,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
): List<OutgoingPacket> { ): List<OutgoingPacket> {
return MessageSvcPbSendMsg.createToStranger(client, contact, message, fragmented, sourceCallback) return MessageSvcPbSendMsg.createToStranger(
client,
contact,
message,
originalMessage,
fragmented,
sourceCallback
)
} }
} }
@ -110,10 +120,11 @@ internal object GroupTempMessageProtocolStrategy : UserMessageProtocolStrategy<N
client: QQAndroidClient, client: QQAndroidClient,
contact: NormalMemberImpl, contact: NormalMemberImpl,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
): List<OutgoingPacket> { ): List<OutgoingPacket> {
return MessageSvcPbSendMsg.createToTemp(client, contact, message, fragmented, sourceCallback) return MessageSvcPbSendMsg.createToTemp(client, contact, message, originalMessage, fragmented, sourceCallback)
} }
} }
@ -124,14 +135,15 @@ internal open class GroupMessageProtocolStrategy(
client: QQAndroidClient, client: QQAndroidClient,
contact: GroupImpl, contact: GroupImpl,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
): List<OutgoingPacket> { ): List<OutgoingPacket> {
return MessageSvcPbSendMsg.createToGroup(client, contact, message, fragmented, sourceCallback) return MessageSvcPbSendMsg.createToGroup(client, contact, message, originalMessage, fragmented, sourceCallback)
} }
override suspend fun constructSourceForSpecialMessage( override suspend fun constructSourceForSpecialMessage(
finalMessage: MessageChain, originalMessage: MessageChain,
fromAppId: Int fromAppId: Int
): OnlineMessageSource.Outgoing { ): OnlineMessageSource.Outgoing {
val receipt: GroupMessageProcessor.SendGroupMessageReceipt = withTimeoutOrNull(3000) { val receipt: GroupMessageProcessor.SendGroupMessageReceipt = withTimeoutOrNull(3000) {
@ -147,7 +159,7 @@ internal open class GroupMessageProtocolStrategy(
sender = contact.bot, sender = contact.bot,
target = contact, target = contact,
time = contact.bot.clock.server.currentTimeSeconds().toInt(), time = contact.bot.clock.server.currentTimeSeconds().toInt(),
originalMessage = finalMessage originalMessage = originalMessage
) )
} }

View File

@ -14,6 +14,7 @@ import net.mamoe.mirai.contact.MessageTooLargeException
import net.mamoe.mirai.internal.contact.AbstractContact import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.SendMessageStep import net.mamoe.mirai.internal.contact.SendMessageStep
import net.mamoe.mirai.internal.message.source.ensureSequenceIdAvailable import net.mamoe.mirai.internal.message.source.ensureSequenceIdAvailable
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
@ -104,6 +105,11 @@ internal interface OutgoingMessagePipelineContext :
*/ */
val ORIGINAL_MESSAGE = TypeKey<Message>("originalMessage") val ORIGINAL_MESSAGE = TypeKey<Message>("originalMessage")
/**
* You should only use [ORIGINAL_MESSAGE_AS_CHAIN] if you can't use [ORIGINAL_MESSAGE]
*/
val ORIGINAL_MESSAGE_AS_CHAIN = TypeKey<MessageChain>("originalMessageAsChain")
/** /**
* Message target * Message target
@ -112,9 +118,8 @@ internal interface OutgoingMessagePipelineContext :
val STEP = TypeKey<SendMessageStep>("step") val STEP = TypeKey<SendMessageStep>("step")
val PROTOCOL_STRATEGY = TypeKey<MessageProtocolStrategy<AbstractContact>>("protocolStrategy") val COMPONENTS = TypeKey<ComponentStorage>("components")
val OutgoingMessagePipelineContext.components: ComponentStorage get() = attributes[COMPONENTS]
val HIGHWAY_UPLOADER = TypeKey<HighwayUploader>("highwayUploader")
} }
} }

View File

@ -10,6 +10,8 @@
package net.mamoe.mirai.internal.network.component package net.mamoe.mirai.internal.network.component
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/** /**
* Mediator for [component][ComponentKey]s accessing each other. * Mediator for [component][ComponentKey]s accessing each other.
@ -38,6 +40,7 @@ internal interface ComponentStorage {
} }
internal fun buildComponentStorage(builderAction: MutableComponentStorage.() -> Unit): ComponentStorage { internal fun buildComponentStorage(builderAction: MutableComponentStorage.() -> Unit): ComponentStorage {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return ConcurrentComponentStorage(builderAction) return ConcurrentComponentStorage(builderAction)
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2021 Mamoe Technologies and contributors. * Copyright 2019-2022 Mamoe Technologies and contributors.
* *
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -12,10 +12,11 @@ package net.mamoe.mirai.internal.network.components
import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.utils.Clock import net.mamoe.mirai.utils.Clock
import net.mamoe.mirai.utils.lateinitMutableProperty
internal class ClockHolder { internal open class ClockHolder {
val local: Clock get() = Clock.SystemDefault open val local: Clock get() = Clock.SystemDefault
var server: Clock = local open var server: Clock by lateinitMutableProperty { local }
companion object : ComponentKey<ClockHolder> { companion object : ComponentKey<ClockHolder> {
val QQAndroidBot.clock get() = components[ClockHolder] val QQAndroidBot.clock get() = components[ClockHolder]

View File

@ -186,6 +186,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient, client: QQAndroidClient,
target: Stranger, target: Stranger,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
source: (OnlineMessageSourceToStrangerImpl) -> Unit, source: (OnlineMessageSourceToStrangerImpl) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -226,7 +227,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
target = target, target = target,
time = client.bot.clock.server.currentTimeSeconds().toInt(), time = client.bot.clock.server.currentTimeSeconds().toInt(),
sequenceIds = sequenceIds.get(), sequenceIds = sequenceIds.get(),
originalMessage = message, originalMessage = originalMessage,
), ),
) )
}, },
@ -242,6 +243,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient, client: QQAndroidClient,
targetFriend: Friend, targetFriend: Friend,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit, crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -290,7 +292,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
target = targetFriend, target = targetFriend,
time = client.bot.clock.server.currentTimeSeconds().toInt(), time = client.bot.clock.server.currentTimeSeconds().toInt(),
sequenceIds = sequenceIds.get(), sequenceIds = sequenceIds.get(),
originalMessage = message, originalMessage = originalMessage,
), ),
) )
}, },
@ -372,6 +374,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient, client: QQAndroidClient,
targetGroup: Group, targetGroup: Group,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit, crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -422,7 +425,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
sender = client.bot, sender = client.bot,
target = targetGroup, target = targetGroup,
time = client.bot.clock.server.currentTimeSeconds().toInt(), time = client.bot.clock.server.currentTimeSeconds().toInt(),
originalMessage = message, //, originalMessage = originalMessage, //,
// sourceMessage = message // sourceMessage = message
), ),
) )
@ -500,6 +503,7 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
client: QQAndroidClient, client: QQAndroidClient,
member: Member, member: Member,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (Deferred<OnlineMessageSourceToTempImpl>) -> Unit, crossinline sourceCallback: (Deferred<OnlineMessageSourceToTempImpl>) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -512,7 +516,7 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
target = member, target = member,
time = client.bot.clock.server.currentTimeSeconds().toInt(), time = client.bot.clock.server.currentTimeSeconds().toInt(),
sequenceIds = intArrayOf(client.atomicNextMessageSequenceId()), sequenceIds = intArrayOf(client.atomicNextMessageSequenceId()),
originalMessage = message, originalMessage = originalMessage,
) )
sourceCallback(CompletableDeferred(source)) sourceCallback(CompletableDeferred(source))
return createToTempImpl( return createToTempImpl(
@ -526,7 +530,8 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
internal inline fun MessageSvcPbSendMsg.createToStranger( internal inline fun MessageSvcPbSendMsg.createToStranger(
client: QQAndroidClient, client: QQAndroidClient,
stranger: Stranger, stranger: Stranger,
message: MessageChain, message: MessageChain, // to send
originalMessage: MessageChain, // for Receipt
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (Deferred<OnlineMessageSourceToStrangerImpl>) -> Unit, crossinline sourceCallback: (Deferred<OnlineMessageSourceToStrangerImpl>) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -537,6 +542,7 @@ internal inline fun MessageSvcPbSendMsg.createToStranger(
client, client,
stranger, stranger,
message, message,
originalMessage,
fragmented, fragmented,
) { sourceCallback(CompletableDeferred(it)) } ) { sourceCallback(CompletableDeferred(it)) }
} }
@ -545,6 +551,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
client: QQAndroidClient, client: QQAndroidClient,
qq: Friend, qq: Friend,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (Deferred<OnlineMessageSourceToFriendImpl>) -> Unit, crossinline sourceCallback: (Deferred<OnlineMessageSourceToFriendImpl>) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -555,6 +562,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
client, client,
qq, qq,
message, message,
originalMessage,
fragmented, fragmented,
) { sourceCallback(CompletableDeferred(it)) } ) { sourceCallback(CompletableDeferred(it)) }
} }
@ -564,6 +572,7 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
client: QQAndroidClient, client: QQAndroidClient,
group: Group, group: Group,
message: MessageChain, message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean, fragmented: Boolean,
crossinline sourceCallback: (Deferred<OnlineMessageSourceToGroupImpl>) -> Unit, crossinline sourceCallback: (Deferred<OnlineMessageSourceToGroupImpl>) -> Unit,
): List<OutgoingPacket> { ): List<OutgoingPacket> {
@ -574,6 +583,7 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
client, client,
group, group,
message, message,
originalMessage,
fragmented, fragmented,
) { sourceCallback(CompletableDeferred(it)) } ) { sourceCallback(CompletableDeferred(it)) }
} }

View File

@ -33,8 +33,11 @@ internal class MessageProtocolFacadeTest : AbstractTest() {
PttMessageProtocol PttMessageProtocol
RichMessageProtocol RichMessageProtocol
TextProtocol TextProtocol
UnsupportedMessageProtocol
VipFaceProtocol VipFaceProtocol
ForwardMessageProtocol
LongMessageProtocol
UnsupportedMessageProtocol
GeneralMessageSenderProtocol
""".trimIndent(), """.trimIndent(),
MessageProtocolFacadeImpl().loaded.joinToString("\n") { it::class.simpleName.toString() } MessageProtocolFacadeImpl().loaded.joinToString("\n") { it::class.simpleName.toString() }
) )

View File

@ -12,8 +12,12 @@ package net.mamoe.mirai.internal.message.protocol.impl
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.contact.ContactOrBot import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.message.data.inferMessageSourceKind import net.mamoe.mirai.internal.message.data.inferMessageSourceKind
import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
@ -21,17 +25,31 @@ import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacadeImpl
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderPipelineImpl import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderPipelineImpl
import net.mamoe.mirai.internal.message.protocol.decodeAndRefineLight import net.mamoe.mirai.internal.message.protocol.decodeAndRefineLight
import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderPipelineImpl import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderPipelineImpl
import net.mamoe.mirai.internal.message.protocol.outgoing.HighwayUploader
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.message.source.OnlineMessageSourceToFriendImpl
import net.mamoe.mirai.internal.message.source.OnlineMessageSourceToGroupImpl
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.internal.notice.processors.GroupExtensions import net.mamoe.mirai.internal.notice.processors.GroupExtensions
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.MessageChainBuilder import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.utils.Clock
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.utils.md5
import net.mamoe.mirai.utils.toUHexString
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.test.Asserter
import kotlin.test.assertEquals
import kotlin.test.asserter
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions { internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions {
@ -60,7 +78,10 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
} }
protected fun facadeOf(vararg protocols: MessageProtocol): MessageProtocolFacade { protected fun facadeOf(vararg protocols: MessageProtocol): MessageProtocolFacade {
return MessageProtocolFacadeImpl(protocols.toList()) return MessageProtocolFacadeImpl(
protocols.toList(),
remark = "MessageProtocolFacade with ${protocols.joinToString { it::class.simpleName!! }}"
)
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -211,5 +232,121 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
// sending // sending
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
init {
components[MessageProtocolStrategy] = object : MessageProtocolStrategy<AbstractContact> {
override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet {
assertEquals(0x123, packet.sequenceId)
return MessageSvcPbSendMsg.Response.SUCCESS
}
override suspend fun createPacketsForGeneralMessage(
client: QQAndroidClient,
contact: AbstractContact,
message: MessageChain,
originalMessage: MessageChain,
fragmented: Boolean,
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
): List<OutgoingPacket> {
sourceCallback(CompletableDeferred(constructSourceForSpecialMessage(originalMessage, 1000)))
return listOf(OutgoingPacket("Test", "test", 0x123, ByteReadPacket.Empty))
}
override suspend fun constructSourceForSpecialMessage(
originalMessage: MessageChain,
fromAppId: Int
): OnlineMessageSource.Outgoing {
return when (val defaultTarget = defaultTarget) {
is Group -> OnlineMessageSourceToGroupImpl(
coroutineScope = defaultTarget,
internalIds = intArrayOf(1),
time = 1,
originalMessage = originalMessage,
sender = bot,
target = defaultTarget
)
is Friend -> OnlineMessageSourceToFriendImpl(
sequenceIds = intArrayOf(1),
internalIds = intArrayOf(1),
time = 1,
originalMessage = originalMessage,
sender = bot,
target = defaultTarget
)
else -> error("Unexpected target: $defaultTarget")
}
}
}
components[HighwayUploader] = object : HighwayUploader {
override suspend fun uploadMessages(
contact: AbstractContact,
components: ComponentStorage,
nodes: Collection<ForwardMessage.INode>,
isLong: Boolean,
senderName: String
): String {
return "(size=${nodes.size})${
nodes.joinToString().replace(bot.id.toString(), "123123").md5().toUHexString("")
}"
}
}
components[ClockHolder] = object : ClockHolder() {
override val local: Clock = object : Clock {
override fun currentTimeMillis(): Long = 160023456
}
}
}
fun runWithFacade(action: suspend MessageProtocolFacade.() -> Unit) {
runBlockingUnit {
facadeOf(*protocols).run { action() }
MessageProtocolFacade.INSTANCE.run { action() }
}
}
companion object {
fun assertMessageEquals(expected: Message, actual: Message) {
val expectedChain = expected.toMessageChain()
val actualChain = actual.toMessageChain()
val message = String.format(
"""
Expected: %s
Actual: %s
""".trimIndent(), expectedChain.render(), actualChain.render()
)
assertEquals(expectedChain.size, actualChain.size, message)
asserter.assertEquals(message, expectedChain, actualChain)
}
fun MessageProtocolFacade.assertMessageEquals(expected: Message, actual: Message) {
val expectedChain = expected.toMessageChain()
val actualChain = actual.toMessageChain()
val message = String.format(
"""
Facade: ${this.remark}
Expected: %s
Actual: %s
""".trimIndent(), expectedChain.render(), actualChain.render()
)
assertEquals(expectedChain.size, actualChain.size, message)
asserter.assertEquals(message, expectedChain, actualChain)
}
inline fun Asserter.assertEquals(crossinline message: () -> String, expected: Any?, actual: Any?) {
assertTrue({ message() + ". Expected <$expected>, actual <$actual>." }, actual == expected)
}
fun MessageChain.render(): String = buildString {
appendLine("size = $size")
for (singleMessage in distinct()) {
val count = this@render.count { it == singleMessage }
appendLine("$count x [${singleMessage::class.simpleName}] $singleMessage")
}
}
}
} }

View File

@ -0,0 +1,89 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.message.protocol.impl
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.internal.message.LightMessageRefiner.dropMiraiInternalFlags
import net.mamoe.mirai.internal.message.data.LongMessageInternal
import net.mamoe.mirai.internal.message.flags.ForceAsLongMessage
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.repeat
import net.mamoe.mirai.message.data.toPlainText
import net.mamoe.mirai.utils.castUp
import net.mamoe.mirai.utils.getRandomString
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
internal class LongMessageProtocolTest : AbstractMessageProtocolTest() {
override val protocols: Array<out MessageProtocol> =
arrayOf(
TextProtocol(),
ImageProtocol(),
LongMessageProtocol(),
GeneralMessageSenderProtocol(),
)
init {
defaultTarget = bot.addGroup(123, 1230003).apply {
addMember(1230003, "user3", MemberPermission.OWNER)
}
}
@Test
fun precondition() {
assertEquals(getRandomString(5000, Random(1)), getRandomString(5000, Random(1)))
assertMessageEquals(
"test".toPlainText() + getRandomString(5000, Random(1)) +
Image("{40A7C56B-45C9-23AE-0CFA-23F095B71035}.jpg").repeat(200),
"test".toPlainText() + getRandomString(5000, Random(1)) +
Image("{40A7C56B-45C9-23AE-0CFA-23F095B71035}.jpg").repeat(200)
)
}
@Test
fun `can convert messages to LongMessageInternal`() {
var message = "test".toPlainText() + getRandomString(5000, Random(1)) +
Image("{40A7C56B-45C9-23AE-0CFA-23F095B71035}.jpg").repeat(200)
message += IgnoreLengthCheck
message += ForceAsLongMessage
runWithFacade {
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
val receipt = receipts.single()
assertMessageEquals(message.dropMiraiInternalFlags(), receipt.source.originalMessage)
assertMessageEquals(
LongMessageInternal(
"""
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<msg serviceID="35" templateID="1" action="viewMultiMsg"
brief="testqGnJ1R..."
m_resid="(size=1)DBD2AB20196EEB631C95DEF40E20C709"
m_fileName="160023" sourceMsgId="0" url=""
flag="3" adverSign="0" multiMsgFlag="1">
<item layout="1">
<title>testqGnJ1R...</title>
<hr hidden="false" style="0"/>
<summary>点击查看完整消息</summary>
</item>
<source name="聊天记录" icon="" action="" appid="-1"/>
</msg>
""".trimIndent(), "(size=1)DBD2AB20196EEB631C95DEF40E20C709"
) + IgnoreLengthCheck + ForceAsLongMessage, context.currentMessageChain
)
}
}
}
}

View File

@ -17,7 +17,10 @@ import net.mamoe.mirai.internal.MockBot
import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.components.EventDispatcher import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.PacketLoggingStrategy
import net.mamoe.mirai.internal.network.components.PacketLoggingStrategyImpl
import net.mamoe.mirai.internal.network.components.SsoProcessor import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.framework.components.TestImagePatcher
import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.handler.state.LoggingStateObserver import net.mamoe.mirai.internal.network.handler.state.LoggingStateObserver
@ -61,7 +64,8 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT
MiraiLogger.Factory.create(SafeStateObserver::class, "StateObserver errors") MiraiLogger.Factory.create(SafeStateObserver::class, "StateObserver errors")
) )
) )
set(ImagePatcher, ImagePatcher()) set(ImagePatcher, TestImagePatcher())
set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot))
} }
fun NetworkHandler.assertState(state: NetworkHandler.State) { fun NetworkHandler.assertState(state: NetworkHandler.State) {

View File

@ -16,7 +16,9 @@ import net.mamoe.mirai.internal.message.protocol.impl.GeneralMessageSenderProtoc
import net.mamoe.mirai.internal.message.protocol.outgoing.* import net.mamoe.mirai.internal.message.protocol.outgoing.*
import net.mamoe.mirai.internal.message.source.OnlineMessageSourceToGroupImpl import net.mamoe.mirai.internal.message.source.OnlineMessageSourceToGroupImpl
import net.mamoe.mirai.internal.message.source.createMessageReceipt import net.mamoe.mirai.internal.message.source.createMessageReceipt
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.components.ClockHolder
import net.mamoe.mirai.internal.notice.processors.GroupExtensions import net.mamoe.mirai.internal.notice.processors.GroupExtensions
import net.mamoe.mirai.internal.pipeline.replaceProcessor import net.mamoe.mirai.internal.pipeline.replaceProcessor
import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.internal.test.AbstractTest
@ -24,6 +26,7 @@ import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.ForwardMessage import net.mamoe.mirai.message.data.ForwardMessage
import net.mamoe.mirai.message.data.buildForwardMessage import net.mamoe.mirai.message.data.buildForwardMessage
import net.mamoe.mirai.message.data.toMessageChain import net.mamoe.mirai.message.data.toMessageChain
import net.mamoe.mirai.utils.Clock
import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.currentTimeSeconds
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -79,15 +82,21 @@ internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
set(HighwayUploader, object : HighwayUploader { set(HighwayUploader, object : HighwayUploader {
override suspend fun uploadMessages( override suspend fun uploadMessages(
contact: AbstractContact, contact: AbstractContact,
strategy: MessageProtocolStrategy<*>, components: ComponentStorage,
nodes: Collection<ForwardMessage.INode>, nodes: Collection<ForwardMessage.INode>,
isLong: Boolean, isLong: Boolean,
facade: MessageProtocolFacade,
senderName: String senderName: String
): String { ): String {
return "id" return "id"
} }
}) })
set(ClockHolder, object : ClockHolder() {
override val local: Clock = object : Clock {
override fun currentTimeMillis(): Long {
return 160023456
}
}
})
}) })
assertIs<ForwardMessage>(result.source.originalMessage[ForwardMessage]) assertIs<ForwardMessage>(result.source.originalMessage[ForwardMessage])