mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-04 06:10:07 +08:00
Add LongMessageProtocolTest and various improvements:
Change attributes carrying helper objects to components Make ClockHolder open Use originalMessage for MessageReceipt
This commit is contained in:
parent
3b7eb40529
commit
c8fb354d13
@ -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 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* 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 fun currentTimeMillis(): Long
|
||||
public fun currentTimeSeconds(): Long
|
||||
public fun currentTimeSeconds(): Long = currentTimeMillis() / 1000
|
||||
|
||||
public object SystemDefault : Clock {
|
||||
override fun currentTimeMillis(): Long = net.mamoe.mirai.utils.currentTimeMillis()
|
||||
|
@ -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.network.component.buildComponentStorage
|
||||
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.Highway
|
||||
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 {
|
||||
set(MessageProtocolStrategy, messageProtocolStrategy)
|
||||
set(HighwayUploader, HighwayUploader.Default)
|
||||
set(ClockHolder, bot.components[ClockHolder])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -45,16 +45,27 @@ internal suspend fun <C : Contact> C.broadcastMessagePreSendEvent(
|
||||
|
||||
|
||||
internal enum class SendMessageStep {
|
||||
/**
|
||||
* 尝试单包直接发送全部消息
|
||||
*/
|
||||
FIRST {
|
||||
override fun nextStepOrNull(): SendMessageStep {
|
||||
return LONG_MESSAGE
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 尝试通过长消息通道上传长消息取得 resId 后再通过普通消息通道发送长消息标识
|
||||
*/
|
||||
LONG_MESSAGE {
|
||||
override fun nextStepOrNull(): SendMessageStep {
|
||||
return FRAGMENTED
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送分片多包发送
|
||||
*/
|
||||
FRAGMENTED {
|
||||
override fun nextStepOrNull(): SendMessageStep? {
|
||||
return null
|
||||
|
@ -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 {
|
||||
return this.replace("&", "&")
|
||||
}
|
||||
|
@ -17,11 +17,9 @@ import net.mamoe.mirai.internal.contact.impl
|
||||
import net.mamoe.mirai.internal.contact.uin
|
||||
import net.mamoe.mirai.internal.contact.userIdOrNull
|
||||
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.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.ResourceKind
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
@ -42,21 +40,19 @@ import kotlin.random.Random
|
||||
internal open class MultiMsgUploader(
|
||||
val client: QQAndroidClient,
|
||||
val isLong: Boolean,
|
||||
val tmpRand: Random = Random.Default,
|
||||
val facade: MessageProtocolFacade,
|
||||
val random: Random,
|
||||
val contact: Contact,
|
||||
val strategy: MessageProtocolStrategy<*>,
|
||||
val components: ComponentStorage,
|
||||
val senderName: String
|
||||
) {
|
||||
|
||||
protected open fun newUploader(): MultiMsgUploader = MultiMsgUploader(
|
||||
isLong = isLong,
|
||||
client = client,
|
||||
tmpRand = tmpRand,
|
||||
facade = facade,
|
||||
isLong = isLong,
|
||||
random = random,
|
||||
contact = contact,
|
||||
senderName = senderName,
|
||||
strategy = strategy
|
||||
components = components,
|
||||
senderName = senderName
|
||||
)
|
||||
|
||||
val mainMsg = mutableListOf<MsgComm.Msg>()
|
||||
@ -69,7 +65,7 @@ internal open class MultiMsgUploader(
|
||||
protected open fun newNid(): String {
|
||||
var nid: String
|
||||
do {
|
||||
nid = "${tmpRand.nextInt().absoluteValue}"
|
||||
nid = "${random.nextInt().absoluteValue}"
|
||||
} while (nestedMsgs.containsKey(nid))
|
||||
return nid
|
||||
}
|
||||
@ -140,10 +136,7 @@ internal open class MultiMsgUploader(
|
||||
msgChain = convertNestedForwardMessage(nestedForward, msgChain)
|
||||
}
|
||||
|
||||
msgChain = facade.preprocess(contact.impl(), msgChain, buildComponentStorage {
|
||||
set(MessageProtocolStrategy, strategy)
|
||||
set(HighwayUploader, HighwayUploader.Default)
|
||||
})
|
||||
msgChain = components[MessageProtocolFacade].preprocess(contact.impl(), msgChain, components)
|
||||
|
||||
var seq: Int = -1
|
||||
var uid: Int = -1
|
||||
@ -157,8 +150,8 @@ internal open class MultiMsgUploader(
|
||||
if (seq != -1 && uid != -1) {
|
||||
if (existsIds.add(seq.concatAsLong(uid))) break
|
||||
}
|
||||
seq = tmpRand.nextInt().absoluteValue
|
||||
uid = tmpRand.nextInt().absoluteValue
|
||||
seq = random.nextInt().absoluteValue
|
||||
uid = random.nextInt().absoluteValue
|
||||
}
|
||||
|
||||
val msg0 = MsgComm.Msg(
|
||||
@ -176,18 +169,13 @@ internal open class MultiMsgUploader(
|
||||
),
|
||||
msgType = 82, // troop,
|
||||
groupInfo = if (contact is Group) MsgComm.GroupInfo(
|
||||
groupCode = contact.groupCode,
|
||||
groupCard = senderName // Cinnamon
|
||||
groupCode = contact.groupCode, groupCard = senderName // Cinnamon
|
||||
) else null,
|
||||
isSrcMsg = false,
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
), msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = MessageProtocolFacade.encode(
|
||||
msgChain,
|
||||
messageTarget = contact,
|
||||
withGeneralFlags = false,
|
||||
isForward = true
|
||||
msgChain, messageTarget = contact, withGeneralFlags = false, isForward = true
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -197,17 +185,13 @@ internal open class MultiMsgUploader(
|
||||
}
|
||||
|
||||
open fun toMessageValidationData(): MessageValidationData {
|
||||
val msgTransmit = MsgTransmit.PbMultiMsgTransmit(
|
||||
msg = mainMsg,
|
||||
pbItemList = nestedMsgs.asSequence()
|
||||
.map { (name, msgList) ->
|
||||
MsgTransmit.PbMultiMsgItem(
|
||||
fileName = name,
|
||||
buffer = MsgTransmit.PbMultiMsgNew(msgList).toByteArray(MsgTransmit.PbMultiMsgNew.serializer())
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
)
|
||||
val msgTransmit =
|
||||
MsgTransmit.PbMultiMsgTransmit(msg = mainMsg, pbItemList = nestedMsgs.asSequence().map { (name, msgList) ->
|
||||
MsgTransmit.PbMultiMsgItem(
|
||||
fileName = name,
|
||||
buffer = MsgTransmit.PbMultiMsgNew(msgList).toByteArray(MsgTransmit.PbMultiMsgNew.serializer())
|
||||
)
|
||||
}.toList())
|
||||
val bytes = msgTransmit.toByteArray(MsgTransmit.PbMultiMsgTransmit.serializer())
|
||||
|
||||
return MessageValidationData(bytes.gzip())
|
||||
@ -218,27 +202,20 @@ internal open class MultiMsgUploader(
|
||||
|
||||
val response = client.bot.network.sendAndExpect(
|
||||
MultiMsg.ApplyUp.createForGroup(
|
||||
buType = if (isLong) 1 else 2,
|
||||
client = client,
|
||||
messageData = data,
|
||||
dstUin = contact.uin
|
||||
buType = if (isLong) 1 else 2, client = client, messageData = data, dstUin = contact.uin
|
||||
)
|
||||
)
|
||||
|
||||
lateinit var resId: String
|
||||
when (response) {
|
||||
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
|
||||
error(
|
||||
"Internal error: message is too large, but this should be handled before sending. "
|
||||
)
|
||||
is MultiMsg.ApplyUp.Response.MessageTooLarge -> error(
|
||||
"Internal error: message is too large, but this should be handled before sending. "
|
||||
)
|
||||
is MultiMsg.ApplyUp.Response.RequireUpload -> {
|
||||
resId = response.proto.msgResid
|
||||
|
||||
val body = LongMsg.ReqBody(
|
||||
subcmd = 1,
|
||||
platformType = 9,
|
||||
termType = 5,
|
||||
msgUpReq = listOf(
|
||||
subcmd = 1, platformType = 9, termType = 5, msgUpReq = listOf(
|
||||
LongMsg.MsgUpReq(
|
||||
msgType = 3, // group
|
||||
dstUin = contact.uin,
|
||||
@ -253,14 +230,10 @@ internal open class MultiMsgUploader(
|
||||
|
||||
body.toExternalResource().use { resource ->
|
||||
Highway.uploadResourceBdh(
|
||||
bot = client.bot,
|
||||
resource = resource,
|
||||
kind = when (isLong) {
|
||||
bot = client.bot, resource = resource, kind = when (isLong) {
|
||||
true -> ResourceKind.LONG_MESSAGE
|
||||
false -> ResourceKind.FORWARD_MESSAGE
|
||||
},
|
||||
commandId = 27,
|
||||
initialTicket = response.proto.msgSig
|
||||
}, commandId = 27, initialTicket = response.proto.msgSig
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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.encode.*
|
||||
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.buildComponentStorage
|
||||
import net.mamoe.mirai.internal.network.component.withFallback
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessResult
|
||||
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||
@ -39,6 +42,8 @@ import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal interface MessageProtocolFacade {
|
||||
val remark: String get() = "MessageProtocolFacade"
|
||||
|
||||
val encoderPipeline: MessageEncoderPipeline
|
||||
val decoderPipeline: MessageDecoderPipeline
|
||||
val preprocessorPipeline: OutgoingMessagePipeline
|
||||
@ -129,7 +134,8 @@ internal interface MessageProtocolFacade {
|
||||
/**
|
||||
* The default global instance.
|
||||
*/
|
||||
companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl()
|
||||
companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl(),
|
||||
ComponentKey<MessageProtocolFacade>
|
||||
}
|
||||
|
||||
internal fun MessageProtocolFacade.decodeAndRefineLight(
|
||||
@ -150,7 +156,8 @@ internal suspend fun MessageProtocolFacade.decodeAndRefineDeep(
|
||||
|
||||
|
||||
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 {
|
||||
override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl()
|
||||
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(
|
||||
target: C,
|
||||
message: Message,
|
||||
@ -314,9 +330,9 @@ internal class MessageProtocolFacadeImpl(
|
||||
val attributes = buildTypeSafeMap {
|
||||
set(OutgoingMessagePipelineContext.CONTACT, target.impl())
|
||||
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE, message)
|
||||
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE_AS_CHAIN, message.toMessageChain())
|
||||
set(OutgoingMessagePipelineContext.STEP, SendMessageStep.FIRST)
|
||||
set(OutgoingMessagePipelineContext.PROTOCOL_STRATEGY, context[MessageProtocolStrategy].castUp())
|
||||
set(OutgoingMessagePipelineContext.HIGHWAY_UPLOADER, context[HighwayUploader])
|
||||
set(OutgoingMessagePipelineContext.COMPONENTS, thisComponentStorage.withFallback(context))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
@ -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.decode.MessageDecoder
|
||||
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.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.OutgoingMessageTransformer
|
||||
import net.mamoe.mirai.internal.message.source.createMessageReceipt
|
||||
@ -81,11 +83,11 @@ internal class FileMessageProtocol : MessageProtocol() {
|
||||
val contact = attributes[CONTACT]
|
||||
val bot = contact.bot
|
||||
|
||||
val strategy = attributes[PROTOCOL_STRATEGY]
|
||||
val strategy = components[MessageProtocolStrategy]
|
||||
|
||||
val source = coroutineScope {
|
||||
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))
|
||||
|
@ -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.protocol.MessageProtocol
|
||||
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.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.components
|
||||
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.RichMessage
|
||||
import net.mamoe.mirai.message.data.toMessageChain
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
|
||||
internal class ForwardMessageProtocol : MessageProtocol() {
|
||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||
@ -46,16 +46,16 @@ internal class ForwardMessageProtocol : MessageProtocol() {
|
||||
}.asIterable().verifyLength(forward, contact)
|
||||
}
|
||||
|
||||
val resId = attributes[HIGHWAY_UPLOADER].uploadMessages(
|
||||
val resId = components[HighwayUploader].uploadMessages(
|
||||
contact,
|
||||
attributes[PROTOCOL_STRATEGY],
|
||||
components,
|
||||
forward.nodeList,
|
||||
false
|
||||
)
|
||||
|
||||
currentMessageChain = RichMessage.forwardMessage(
|
||||
resId = resId,
|
||||
fileName = currentTimeSeconds().toString(),
|
||||
fileName = components[ClockHolder].local.currentTimeSeconds().toString(),
|
||||
forwardMessage = forward,
|
||||
).toMessageChain()
|
||||
}
|
||||
|
@ -12,14 +12,17 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
import kotlinx.coroutines.Deferred
|
||||
import net.mamoe.mirai.contact.*
|
||||
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.message.protocol.MessageProtocol
|
||||
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.Companion.CONTACT
|
||||
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.components
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageSender
|
||||
import net.mamoe.mirai.internal.message.source.createMessageReceipt
|
||||
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() {
|
||||
markAsConsumed()
|
||||
|
||||
val strategy = attributes[PROTOCOL_STRATEGY]
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val strategy = components[MessageProtocolStrategy] as MessageProtocolStrategy<AbstractContact>
|
||||
val step = attributes[STEP]
|
||||
val contact = attributes[CONTACT]
|
||||
val bot = contact.bot
|
||||
@ -52,9 +56,9 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
||||
client = bot.client,
|
||||
contact = contact,
|
||||
message = currentMessageChain,
|
||||
fragmented = step == SendMessageStep.FRAGMENTED,
|
||||
sourceCallback = { source = it }
|
||||
)
|
||||
originalMessage = attributes[ORIGINAL_MESSAGE_AS_CHAIN],
|
||||
fragmented = step == SendMessageStep.FRAGMENTED
|
||||
) { source = it }
|
||||
|
||||
sendAllPackets(bot, step, contact, packets)
|
||||
|
||||
@ -70,7 +74,7 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
||||
packets: List<OutgoingPacket>
|
||||
) = packets.forEach { packet ->
|
||||
val originalMessage = attributes[ORIGINAL_MESSAGE]
|
||||
val protocolStrategy = attributes[PROTOCOL_STRATEGY]
|
||||
val protocolStrategy = components[MessageProtocolStrategy]
|
||||
val finalMessage = currentMessageChain
|
||||
|
||||
val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response
|
||||
|
@ -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.SendMessageStep
|
||||
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.ForceAsLongMessage
|
||||
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
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.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.components
|
||||
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.RichMessage
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import net.mamoe.mirai.utils.truncated
|
||||
|
||||
internal class LongMessageProtocol : MessageProtocol() {
|
||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||
@ -36,24 +35,25 @@ internal class LongMessageProtocol : MessageProtocol() {
|
||||
convertToLongMessageIfNeeded(
|
||||
currentMessageChain,
|
||||
attributes[STEP],
|
||||
attributes[CONTACT],
|
||||
attributes[PROTOCOL_STRATEGY]
|
||||
attributes[CONTACT]
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to [LongMessageInternal] iff [SendMessageStep.FIRST] has failed.
|
||||
*/
|
||||
private suspend fun OutgoingMessagePipelineContext.convertToLongMessageIfNeeded(
|
||||
chain: MessageChain,
|
||||
step: SendMessageStep,
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>
|
||||
): MessageChain {
|
||||
val uploader = attributes[HIGHWAY_UPLOADER]
|
||||
val uploader = components[HighwayUploader]
|
||||
|
||||
suspend fun sendLongImpl(): MessageChain {
|
||||
val time = currentTimeSeconds()
|
||||
val resId = uploader.uploadLongMessage(contact, strategy, chain, time.toInt())
|
||||
return chain + RichMessage.longMessage(
|
||||
val time = components[ClockHolder].local.currentTimeSeconds()
|
||||
val resId = uploader.uploadLongMessage(contact, components, chain, time.toInt())
|
||||
return chain + createLongMessage(
|
||||
brief = chain.takeContent(27),
|
||||
resId = resId,
|
||||
timeSeconds = time
|
||||
@ -81,4 +81,26 @@ internal class LongMessageProtocol : MessageProtocol() {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
@ -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.encode.MessageEncoder
|
||||
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.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.source.createMessageReceipt
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.MusicSharePacket
|
||||
@ -55,8 +58,8 @@ internal class MusicShareProtocol : MessageProtocol() {
|
||||
val result = bot.network.sendAndExpect(packet)
|
||||
result.pkg.checkSuccess("send music share")
|
||||
|
||||
val strategy = attributes[OutgoingMessagePipelineContext.PROTOCOL_STRATEGY]
|
||||
val source = strategy.constructSourceForSpecialMessage(currentMessageChain, 3116)
|
||||
val strategy = components[MessageProtocolStrategy]
|
||||
val source = strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 3116)
|
||||
source.tryEnsureSequenceIdAvailable()
|
||||
|
||||
collect(source.createMessageReceipt(contact, true))
|
||||
|
@ -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.nickIn
|
||||
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.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.MessageChain
|
||||
import kotlin.random.Random
|
||||
|
||||
internal interface HighwayUploader {
|
||||
suspend fun uploadMessages(
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>,
|
||||
components: ComponentStorage,
|
||||
nodes: Collection<ForwardMessage.INode>,
|
||||
isLong: Boolean,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade,
|
||||
senderName: String = contact.bot.nickIn(contact),
|
||||
): String {
|
||||
nodes.forEach { it.messageChain.ensureSequenceIdAvailable() }
|
||||
@ -32,10 +33,10 @@ internal interface HighwayUploader {
|
||||
val uploader = MultiMsgUploader(
|
||||
client = contact.bot.client,
|
||||
isLong = isLong,
|
||||
facade = facade,
|
||||
contact = contact,
|
||||
random = Random(components[ClockHolder].local.currentTimeSeconds()),
|
||||
senderName = senderName,
|
||||
strategy = strategy
|
||||
components = components
|
||||
).also { it.emitMain(nodes) }
|
||||
|
||||
return uploader.uploadAndReturnResId()
|
||||
@ -43,7 +44,7 @@ internal interface HighwayUploader {
|
||||
|
||||
suspend fun uploadLongMessage(
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>,
|
||||
components: ComponentStorage,
|
||||
chain: MessageChain,
|
||||
timeSeconds: Int,
|
||||
senderName: String = contact.bot.nickIn(contact),
|
||||
@ -51,7 +52,7 @@ internal interface HighwayUploader {
|
||||
val bot = contact.bot
|
||||
return uploadMessages(
|
||||
contact,
|
||||
strategy,
|
||||
components,
|
||||
listOf(
|
||||
ForwardMessage.Node(
|
||||
senderId = bot.id,
|
||||
@ -61,7 +62,7 @@ internal interface HighwayUploader {
|
||||
)
|
||||
),
|
||||
true,
|
||||
senderName = senderName
|
||||
senderName = senderName,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -37,13 +37,14 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
|
||||
suspend fun createPacketsForGeneralMessage(
|
||||
client: QQAndroidClient,
|
||||
contact: C,
|
||||
message: MessageChain,
|
||||
message: MessageChain, // to send
|
||||
originalMessage: MessageChain, // to create Receipt
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit,
|
||||
): List<OutgoingPacket>
|
||||
|
||||
suspend fun constructSourceForSpecialMessage(
|
||||
finalMessage: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fromAppId: Int,
|
||||
): OnlineMessageSource.Outgoing
|
||||
|
||||
@ -52,7 +53,7 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
|
||||
|
||||
internal sealed class UserMessageProtocolStrategy<C : AbstractUser> : MessageProtocolStrategy<C> {
|
||||
override suspend fun constructSourceForSpecialMessage(
|
||||
finalMessage: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fromAppId: Int
|
||||
): OnlineMessageSource.Outgoing {
|
||||
throw UnsupportedOperationException("Sending MusicShare or FileMessage to User is not yet supported")
|
||||
@ -66,14 +67,15 @@ internal class FriendMessageProtocolStrategy(
|
||||
client: QQAndroidClient,
|
||||
contact: FriendImpl,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): List<OutgoingPacket> {
|
||||
return MessageSvcPbSendMsg.createToFriend(client, contact, message, fragmented, sourceCallback)
|
||||
return MessageSvcPbSendMsg.createToFriend(client, contact, message, originalMessage, fragmented, sourceCallback)
|
||||
}
|
||||
|
||||
override suspend fun constructSourceForSpecialMessage(
|
||||
finalMessage: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fromAppId: Int
|
||||
): OnlineMessageSource.Outgoing {
|
||||
val receipt: PrivateMessageProcessor.SendPrivateMessageReceipt = withTimeoutOrNull(3000) {
|
||||
@ -88,7 +90,7 @@ internal class FriendMessageProtocolStrategy(
|
||||
sender = contact.bot,
|
||||
target = contact,
|
||||
time = contact.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
originalMessage = finalMessage
|
||||
originalMessage = originalMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -98,10 +100,18 @@ internal object StrangerMessageProtocolStrategy : UserMessageProtocolStrategy<St
|
||||
client: QQAndroidClient,
|
||||
contact: StrangerImpl,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): 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,
|
||||
contact: NormalMemberImpl,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): 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,
|
||||
contact: GroupImpl,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): List<OutgoingPacket> {
|
||||
return MessageSvcPbSendMsg.createToGroup(client, contact, message, fragmented, sourceCallback)
|
||||
return MessageSvcPbSendMsg.createToGroup(client, contact, message, originalMessage, fragmented, sourceCallback)
|
||||
}
|
||||
|
||||
override suspend fun constructSourceForSpecialMessage(
|
||||
finalMessage: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fromAppId: Int
|
||||
): OnlineMessageSource.Outgoing {
|
||||
val receipt: GroupMessageProcessor.SendGroupMessageReceipt = withTimeoutOrNull(3000) {
|
||||
@ -147,7 +159,7 @@ internal open class GroupMessageProtocolStrategy(
|
||||
sender = contact.bot,
|
||||
target = contact,
|
||||
time = contact.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
originalMessage = finalMessage
|
||||
originalMessage = originalMessage
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import net.mamoe.mirai.contact.MessageTooLargeException
|
||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||
import net.mamoe.mirai.internal.contact.SendMessageStep
|
||||
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.pipeline.AbstractProcessorPipeline
|
||||
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
|
||||
@ -104,6 +105,11 @@ internal interface OutgoingMessagePipelineContext :
|
||||
*/
|
||||
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
|
||||
@ -112,9 +118,8 @@ internal interface OutgoingMessagePipelineContext :
|
||||
|
||||
val STEP = TypeKey<SendMessageStep>("step")
|
||||
|
||||
val PROTOCOL_STRATEGY = TypeKey<MessageProtocolStrategy<AbstractContact>>("protocolStrategy")
|
||||
|
||||
val HIGHWAY_UPLOADER = TypeKey<HighwayUploader>("highwayUploader")
|
||||
val COMPONENTS = TypeKey<ComponentStorage>("components")
|
||||
val OutgoingMessagePipelineContext.components: ComponentStorage get() = attributes[COMPONENTS]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
package net.mamoe.mirai.internal.network.component
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Mediator for [component][ComponentKey]s accessing each other.
|
||||
@ -38,6 +40,7 @@ internal interface ComponentStorage {
|
||||
}
|
||||
|
||||
internal fun buildComponentStorage(builderAction: MutableComponentStorage.() -> Unit): ComponentStorage {
|
||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||
return ConcurrentComponentStorage(builderAction)
|
||||
}
|
||||
|
||||
|
@ -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 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* 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.network.component.ComponentKey
|
||||
import net.mamoe.mirai.utils.Clock
|
||||
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||
|
||||
internal class ClockHolder {
|
||||
val local: Clock get() = Clock.SystemDefault
|
||||
var server: Clock = local
|
||||
internal open class ClockHolder {
|
||||
open val local: Clock get() = Clock.SystemDefault
|
||||
open var server: Clock by lateinitMutableProperty { local }
|
||||
|
||||
companion object : ComponentKey<ClockHolder> {
|
||||
val QQAndroidBot.clock get() = components[ClockHolder]
|
||||
|
@ -186,6 +186,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
client: QQAndroidClient,
|
||||
target: Stranger,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
source: (OnlineMessageSourceToStrangerImpl) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -226,7 +227,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
target = target,
|
||||
time = client.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
sequenceIds = sequenceIds.get(),
|
||||
originalMessage = message,
|
||||
originalMessage = originalMessage,
|
||||
),
|
||||
)
|
||||
},
|
||||
@ -242,6 +243,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
client: QQAndroidClient,
|
||||
targetFriend: Friend,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -290,7 +292,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
target = targetFriend,
|
||||
time = client.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
sequenceIds = sequenceIds.get(),
|
||||
originalMessage = message,
|
||||
originalMessage = originalMessage,
|
||||
),
|
||||
)
|
||||
},
|
||||
@ -372,6 +374,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
client: QQAndroidClient,
|
||||
targetGroup: Group,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -422,7 +425,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
sender = client.bot,
|
||||
target = targetGroup,
|
||||
time = client.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
originalMessage = message, //,
|
||||
originalMessage = originalMessage, //,
|
||||
// sourceMessage = message
|
||||
),
|
||||
)
|
||||
@ -500,6 +503,7 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
|
||||
client: QQAndroidClient,
|
||||
member: Member,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (Deferred<OnlineMessageSourceToTempImpl>) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -512,7 +516,7 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
|
||||
target = member,
|
||||
time = client.bot.clock.server.currentTimeSeconds().toInt(),
|
||||
sequenceIds = intArrayOf(client.atomicNextMessageSequenceId()),
|
||||
originalMessage = message,
|
||||
originalMessage = originalMessage,
|
||||
)
|
||||
sourceCallback(CompletableDeferred(source))
|
||||
return createToTempImpl(
|
||||
@ -526,7 +530,8 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
|
||||
internal inline fun MessageSvcPbSendMsg.createToStranger(
|
||||
client: QQAndroidClient,
|
||||
stranger: Stranger,
|
||||
message: MessageChain,
|
||||
message: MessageChain, // to send
|
||||
originalMessage: MessageChain, // for Receipt
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (Deferred<OnlineMessageSourceToStrangerImpl>) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -537,6 +542,7 @@ internal inline fun MessageSvcPbSendMsg.createToStranger(
|
||||
client,
|
||||
stranger,
|
||||
message,
|
||||
originalMessage,
|
||||
fragmented,
|
||||
) { sourceCallback(CompletableDeferred(it)) }
|
||||
}
|
||||
@ -545,6 +551,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
|
||||
client: QQAndroidClient,
|
||||
qq: Friend,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (Deferred<OnlineMessageSourceToFriendImpl>) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -555,6 +562,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
|
||||
client,
|
||||
qq,
|
||||
message,
|
||||
originalMessage,
|
||||
fragmented,
|
||||
) { sourceCallback(CompletableDeferred(it)) }
|
||||
}
|
||||
@ -564,6 +572,7 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
|
||||
client: QQAndroidClient,
|
||||
group: Group,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
crossinline sourceCallback: (Deferred<OnlineMessageSourceToGroupImpl>) -> Unit,
|
||||
): List<OutgoingPacket> {
|
||||
@ -574,6 +583,7 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
|
||||
client,
|
||||
group,
|
||||
message,
|
||||
originalMessage,
|
||||
fragmented,
|
||||
) { sourceCallback(CompletableDeferred(it)) }
|
||||
}
|
@ -33,8 +33,11 @@ internal class MessageProtocolFacadeTest : AbstractTest() {
|
||||
PttMessageProtocol
|
||||
RichMessageProtocol
|
||||
TextProtocol
|
||||
UnsupportedMessageProtocol
|
||||
VipFaceProtocol
|
||||
ForwardMessageProtocol
|
||||
LongMessageProtocol
|
||||
UnsupportedMessageProtocol
|
||||
GeneralMessageSenderProtocol
|
||||
""".trimIndent(),
|
||||
MessageProtocolFacadeImpl().loaded.joinToString("\n") { it::class.simpleName.toString() }
|
||||
)
|
||||
|
@ -12,8 +12,12 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.contact.ContactOrBot
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
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.protocol.MessageProtocol
|
||||
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.decodeAndRefineLight
|
||||
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.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.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageChainBuilder
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.internal.test.runBlockingUnit
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.Clock
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.test.Asserter
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.asserter
|
||||
|
||||
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions {
|
||||
|
||||
@ -60,7 +78,10 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
}
|
||||
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,10 @@ import net.mamoe.mirai.internal.MockBot
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
|
||||
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.framework.components.TestImagePatcher
|
||||
import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor
|
||||
import net.mamoe.mirai.internal.network.handler.NetworkHandler
|
||||
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")
|
||||
)
|
||||
)
|
||||
set(ImagePatcher, ImagePatcher())
|
||||
set(ImagePatcher, TestImagePatcher())
|
||||
set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot))
|
||||
}
|
||||
|
||||
fun NetworkHandler.assertState(state: NetworkHandler.State) {
|
||||
|
@ -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.source.OnlineMessageSourceToGroupImpl
|
||||
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.components.ClockHolder
|
||||
import net.mamoe.mirai.internal.notice.processors.GroupExtensions
|
||||
import net.mamoe.mirai.internal.pipeline.replaceProcessor
|
||||
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.buildForwardMessage
|
||||
import net.mamoe.mirai.message.data.toMessageChain
|
||||
import net.mamoe.mirai.utils.Clock
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -79,15 +82,21 @@ internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
||||
set(HighwayUploader, object : HighwayUploader {
|
||||
override suspend fun uploadMessages(
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>,
|
||||
components: ComponentStorage,
|
||||
nodes: Collection<ForwardMessage.INode>,
|
||||
isLong: Boolean,
|
||||
facade: MessageProtocolFacade,
|
||||
senderName: String
|
||||
): String {
|
||||
return "id"
|
||||
}
|
||||
})
|
||||
set(ClockHolder, object : ClockHolder() {
|
||||
override val local: Clock = object : Clock {
|
||||
override fun currentTimeMillis(): Long {
|
||||
return 160023456
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
assertIs<ForwardMessage>(result.source.originalMessage[ForwardMessage])
|
||||
|
Loading…
Reference in New Issue
Block a user