mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-04 15:40:13 +08:00
Add more MessageProtocolTest:
Add fragmented test Fix MusicShareProtocol and implement sending tests for MusicShareProtocolTest OutgoingMessagePipelineImpl: set stopWhenConsumed Fix `currentMessageChain` not updated in `processAlso`, add strong message packets checks, add tests for converting messages failed to send at FIRST step to LongMessageInternal Add notes for receiving ForwardMessage Add facade parameters to `download ForwardMessage*` Add ForwardMessageProtocolTest Add QuoteReplyProtocolTest Add CONTAINING_MSG for MessageDecoderContext.attributes, for information-use only Fix MessageReceiptTest Fix QuoteReplyProtocolTest
This commit is contained in:
parent
38465972ba
commit
ab3280f6b7
@ -44,11 +44,13 @@ internal suspend fun <C : Contact> C.broadcastMessagePreSendEvent(
|
||||
}
|
||||
|
||||
|
||||
internal enum class SendMessageStep {
|
||||
internal enum class SendMessageStep(
|
||||
val allowMultiplePackets: Boolean
|
||||
) {
|
||||
/**
|
||||
* 尝试单包直接发送全部消息
|
||||
*/
|
||||
FIRST {
|
||||
FIRST(false) {
|
||||
override fun nextStepOrNull(): SendMessageStep {
|
||||
return LONG_MESSAGE
|
||||
}
|
||||
@ -57,7 +59,7 @@ internal enum class SendMessageStep {
|
||||
/**
|
||||
* 尝试通过长消息通道上传长消息取得 resId 后再通过普通消息通道发送长消息标识
|
||||
*/
|
||||
LONG_MESSAGE {
|
||||
LONG_MESSAGE(false) {
|
||||
override fun nextStepOrNull(): SendMessageStep {
|
||||
return FRAGMENTED
|
||||
}
|
||||
@ -66,7 +68,7 @@ internal enum class SendMessageStep {
|
||||
/**
|
||||
* 发送分片多包发送
|
||||
*/
|
||||
FRAGMENTED {
|
||||
FRAGMENTED(true) {
|
||||
override fun nextStepOrNull(): SendMessageStep? {
|
||||
return null
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
|
||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.joinToMessageChain
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.toAudio
|
||||
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
||||
import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
|
||||
@ -34,10 +33,11 @@ internal fun ImMsgBody.SourceMsg.toMessageChainNoSource(
|
||||
messageSourceKind: MessageSourceKind,
|
||||
groupIdOrZero: Long,
|
||||
refineContext: RefineContext = EmptyRefineContext,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade
|
||||
): MessageChain {
|
||||
val elements = this.elems
|
||||
return buildMessageChain(elements.size + 1) {
|
||||
joinToMessageChain(elements, groupIdOrZero, messageSourceKind, bot, this)
|
||||
facade.decode(elements, groupIdOrZero, messageSourceKind, bot, this, null)
|
||||
}.cleanupRubbishMessageElements().refineLight(bot, refineContext)
|
||||
}
|
||||
|
||||
@ -47,13 +47,15 @@ internal suspend fun List<MsgComm.Msg>.toMessageChainOnline(
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
refineContext: RefineContext = EmptyRefineContext,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade
|
||||
): MessageChain {
|
||||
return toMessageChain(bot, groupIdOrZero, true, messageSourceKind).refineDeep(bot, refineContext)
|
||||
return toMessageChain(bot, groupIdOrZero, true, messageSourceKind, facade).refineDeep(bot, refineContext)
|
||||
}
|
||||
|
||||
internal suspend fun MsgComm.Msg.toMessageChainOnline(
|
||||
bot: Bot,
|
||||
refineContext: RefineContext = EmptyRefineContext,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade,
|
||||
): MessageChain {
|
||||
fun getSourceKind(c2cCmd: Int): MessageSourceKind {
|
||||
return when (c2cCmd) {
|
||||
@ -69,7 +71,7 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline(
|
||||
MessageSourceKind.GROUP -> msgHead.groupInfo?.groupCode ?: 0
|
||||
else -> 0
|
||||
}
|
||||
return listOf(this).toMessageChainOnline(bot, groupId, kind, refineContext)
|
||||
return listOf(this).toMessageChainOnline(bot, groupId, kind, refineContext, facade)
|
||||
}
|
||||
|
||||
//internal fun List<MsgComm.Msg>.toMessageChainOffline(
|
||||
@ -95,20 +97,21 @@ private fun List<MsgComm.Msg>.toMessageChain(
|
||||
groupIdOrZero: Long,
|
||||
onlineSource: Boolean?,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade,
|
||||
): MessageChain {
|
||||
val messageList = this
|
||||
|
||||
|
||||
val elements = messageList.flatMap { it.msgBody.richText.elems }
|
||||
|
||||
val builder = MessageChainBuilder(elements.size)
|
||||
val builder = MessageChainBuilder(messageList.sumOf { it.msgBody.richText.elems.size })
|
||||
|
||||
if (onlineSource != null) {
|
||||
builder.add(ReceiveMessageTransformer.createMessageSource(bot, onlineSource, messageSourceKind, messageList))
|
||||
}
|
||||
|
||||
|
||||
joinToMessageChain(elements, groupIdOrZero, messageSourceKind, bot, builder)
|
||||
messageList.forEach { msg ->
|
||||
facade.decode(msg.msgBody.richText.elems, groupIdOrZero, messageSourceKind, bot, builder, msg)
|
||||
}
|
||||
|
||||
for (msg in messageList) {
|
||||
msg.msgBody.richText.ptt?.toAudio()?.let { builder.add(it) }
|
||||
@ -143,16 +146,6 @@ internal object ReceiveMessageTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
fun joinToMessageChain(
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
bot: Bot,
|
||||
builder: MessageChainBuilder,
|
||||
) {
|
||||
return MessageProtocolFacade.decode(elements, groupIdOrZero, messageSourceKind, bot, builder)
|
||||
}
|
||||
|
||||
fun MessageChainBuilder.compressContinuousPlainText() {
|
||||
var index = 0
|
||||
val builder = StringBuilder()
|
||||
|
@ -12,10 +12,7 @@
|
||||
package net.mamoe.mirai.internal.message.flags
|
||||
|
||||
import net.mamoe.mirai.internal.message.visitor.ex
|
||||
import net.mamoe.mirai.message.data.AbstractMessageKey
|
||||
import net.mamoe.mirai.message.data.ConstrainSingle
|
||||
import net.mamoe.mirai.message.data.MessageKey
|
||||
import net.mamoe.mirai.message.data.MessageMetadata
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
@ -24,17 +21,35 @@ import net.mamoe.mirai.utils.safeCast
|
||||
*/
|
||||
internal sealed interface InternalFlagOnlyMessage : MessageMetadata
|
||||
|
||||
internal sealed interface ForceAs : InternalFlagOnlyMessage, ConstrainSingle {
|
||||
companion object Key : AbstractMessageKey<ForceAs>({ it.safeCast() })
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部 flag, 放入 chain 强制作为 long 发送
|
||||
*/
|
||||
internal object ForceAsLongMessage : MessageMetadata, ConstrainSingle, InternalFlagOnlyMessage,
|
||||
AbstractMessageKey<ForceAsLongMessage>({ it.safeCast() }) {
|
||||
internal object ForceAsLongMessage : ForceAs,
|
||||
AbstractPolymorphicMessageKey<ForceAs, ForceAsLongMessage>(ForceAs, { it.safeCast() }) {
|
||||
override val key: MessageKey<ForceAsLongMessage> get() = this
|
||||
|
||||
override fun toString(): String = ""
|
||||
|
||||
override fun <D, R> accept(visitor: MessageVisitor<D, R>, data: D): R {
|
||||
return visitor.ex()?.visitForceAsLongMessage(this, data) ?: super<InternalFlagOnlyMessage>.accept(visitor, data)
|
||||
return visitor.ex()?.visitForceAsLongMessage(this, data) ?: super.accept(visitor, data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部 flag, 放入 chain 强制作为 fragmented 发送
|
||||
*/
|
||||
internal object ForceAsFragmentedMessage : ForceAs,
|
||||
AbstractPolymorphicMessageKey<ForceAs, ForceAsFragmentedMessage>(ForceAs, { it.safeCast() }) {
|
||||
override val key: MessageKey<ForceAsFragmentedMessage> get() = this
|
||||
|
||||
override fun toString(): String = ""
|
||||
|
||||
override fun <D, R> accept(visitor: MessageVisitor<D, R>, data: D): R {
|
||||
return visitor.ex()?.visitForceAsFragmentedMessage(this, data) ?: super.accept(visitor, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,19 @@ import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePreproc
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageSender
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.systemProp
|
||||
import net.mamoe.mirai.utils.withSwitch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
// Loaded by ServiceLoader
|
||||
internal abstract class MessageProtocol(
|
||||
val priority: UInt = PRIORITY_CONTENT // the higher, the prior it being called
|
||||
) {
|
||||
val logger: MiraiLogger by lazy {
|
||||
MiraiLogger.Factory.create(this::class).withSwitch(systemProp("mirai.message.protocol.log.full", false))
|
||||
}
|
||||
|
||||
fun collectProcessors(processorCollector: ProcessorCollector) {
|
||||
processorCollector.collectProcessorsImpl()
|
||||
}
|
||||
|
@ -22,11 +22,18 @@ 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.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.COMPONENTS
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.MESSAGE_TO_RETRY
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE
|
||||
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.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.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessResult
|
||||
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||
import net.mamoe.mirai.internal.utils.structureToString
|
||||
@ -34,10 +41,7 @@ import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor
|
||||
import net.mamoe.mirai.message.data.visitor.accept
|
||||
import net.mamoe.mirai.utils.MutableTypeSafeMap
|
||||
import net.mamoe.mirai.utils.TestOnly
|
||||
import net.mamoe.mirai.utils.buildTypeSafeMap
|
||||
import net.mamoe.mirai.utils.castUp
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ -72,6 +76,7 @@ internal interface MessageProtocolFacade {
|
||||
messageSourceKind: MessageSourceKind,
|
||||
bot: Bot,
|
||||
builder: MessageChainBuilder,
|
||||
containingMsg: MsgComm.Msg? = null,
|
||||
)
|
||||
|
||||
|
||||
@ -127,7 +132,7 @@ internal interface MessageProtocolFacade {
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
bot: Bot,
|
||||
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this) }
|
||||
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) }
|
||||
|
||||
fun copy(): MessageProtocolFacade
|
||||
|
||||
@ -235,7 +240,8 @@ internal class MessageProtocolFacadeImpl(
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
bot: Bot,
|
||||
builder: MessageChainBuilder
|
||||
builder: MessageChainBuilder,
|
||||
containingMsg: MsgComm.Msg?
|
||||
) {
|
||||
val pipeline = decoderPipeline
|
||||
|
||||
@ -243,6 +249,7 @@ internal class MessageProtocolFacadeImpl(
|
||||
set(MessageDecoderContext.BOT, bot)
|
||||
set(MessageDecoderContext.MESSAGE_SOURCE_KIND, messageSourceKind)
|
||||
set(MessageDecoderContext.GROUP_ID, groupIdOrZero)
|
||||
set(MessageDecoderContext.CONTAINING_MSG, containingMsg)
|
||||
}
|
||||
|
||||
runCoroutineInPlace {
|
||||
@ -297,8 +304,15 @@ internal class MessageProtocolFacadeImpl(
|
||||
): ProcessResult<OutgoingMessagePipelineContext, MessageReceipt<*>> {
|
||||
val attributes = createAttributesForOutgoingMessage(target, message, components)
|
||||
|
||||
val (context, _) = preprocessorPipeline.process(message.toMessageChain(), attributes)
|
||||
return outgoingPipeline.process(message.toMessageChain(), context, attributes)
|
||||
val data = message.toMessageChain()
|
||||
val (context, _) = preprocessorPipeline.process(data, attributes)
|
||||
val preprocessed = context.currentMessageChain
|
||||
|
||||
return outgoingPipeline.process(
|
||||
data,
|
||||
outgoingPipeline.createContext(preprocessed, context.attributes.plus(MESSAGE_TO_RETRY to preprocessed)),
|
||||
attributes
|
||||
)
|
||||
}
|
||||
|
||||
override fun copy(): MessageProtocolFacade {
|
||||
@ -327,12 +341,14 @@ internal class MessageProtocolFacadeImpl(
|
||||
message: Message,
|
||||
context: ComponentStorage
|
||||
): MutableTypeSafeMap {
|
||||
val chain = message.toMessageChain()
|
||||
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.COMPONENTS, thisComponentStorage.withFallback(context))
|
||||
set(CONTACT, target.impl())
|
||||
set(ORIGINAL_MESSAGE, message)
|
||||
set(ORIGINAL_MESSAGE_AS_CHAIN, chain)
|
||||
set(STEP, SendMessageStep.FIRST)
|
||||
set(COMPONENTS, thisComponentStorage.withFallback(context))
|
||||
set(MESSAGE_TO_RETRY, chain)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
@ -10,11 +10,11 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.decode
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderContext.Companion.CONTAINING_MSG
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline
|
||||
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.internal.pipeline.*
|
||||
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.utils.*
|
||||
@ -29,6 +29,7 @@ internal interface MessageDecoderContext : ProcessorPipelineContext<ImMsgBody.El
|
||||
val BOT = TypeKey<Bot>("bot")
|
||||
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
||||
val GROUP_ID = TypeKey<Long>("groupId") // zero if not group
|
||||
val CONTAINING_MSG = TypeKey<MsgComm.Msg?>("containingMsg")
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +46,17 @@ internal open class MessageDecoderPipelineImpl :
|
||||
override fun createContext(data: ImMsgBody.Elem, attributes: TypeSafeMap): MessageDecoderContext =
|
||||
MessageDecoderContextImpl(attributes)
|
||||
|
||||
override suspend fun process(
|
||||
data: ImMsgBody.Elem,
|
||||
context: MessageDecoderContext,
|
||||
attributes: TypeSafeMap
|
||||
): ProcessResult<MessageDecoderContext, Message> {
|
||||
context.attributes[CONTAINING_MSG]?.let { msg ->
|
||||
traceLogging.info { "Processing MsgCommon.Msg: ${msg.structureToStringAndDesensitizeIfAvailable()}" }
|
||||
}
|
||||
return super.process(data, context, attributes)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@TestOnly
|
||||
val defaultTraceLogging: MiraiLoggerWithSwitch by lazy {
|
||||
|
@ -53,7 +53,7 @@ internal class ForwardMessageProtocol : MessageProtocol() {
|
||||
false
|
||||
)
|
||||
|
||||
currentMessageChain = RichMessage.forwardMessage(
|
||||
currentMessageChain += RichMessage.forwardMessage(
|
||||
resId = resId,
|
||||
fileName = components[ClockHolder].local.currentTimeSeconds().toString(),
|
||||
forwardMessage = forward,
|
||||
|
@ -14,11 +14,13 @@ 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.flags.ForceAsFragmentedMessage
|
||||
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.MESSAGE_TO_RETRY
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE
|
||||
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
|
||||
@ -30,17 +32,20 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcP
|
||||
import net.mamoe.mirai.message.data.AtAll
|
||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||
import net.mamoe.mirai.message.data.content
|
||||
import net.mamoe.mirai.message.data.toMessageChain
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.buildTypeSafeMap
|
||||
import net.mamoe.mirai.utils.info
|
||||
import net.mamoe.mirai.utils.truncated
|
||||
|
||||
internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_SENDER) {
|
||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||
add(GeneralMessageSender())
|
||||
add(GeneralMessageSender(logger))
|
||||
}
|
||||
|
||||
|
||||
class GeneralMessageSender : OutgoingMessageSender {
|
||||
class GeneralMessageSender(
|
||||
private val logger: MiraiLogger,
|
||||
) : OutgoingMessageSender {
|
||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||
markAsConsumed()
|
||||
|
||||
@ -57,28 +62,49 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
||||
contact = contact,
|
||||
message = currentMessageChain,
|
||||
originalMessage = attributes[ORIGINAL_MESSAGE_AS_CHAIN],
|
||||
fragmented = step == SendMessageStep.FRAGMENTED
|
||||
fragmented = step == SendMessageStep.FRAGMENTED || currentMessageChain.contains(ForceAsFragmentedMessage)
|
||||
) { source = it }
|
||||
|
||||
sendAllPackets(bot, step, contact, packets)
|
||||
|
||||
val sourceAwait = source?.await() ?: error("Internal error: source is not initialized")
|
||||
sourceAwait.tryEnsureSequenceIdAvailable()
|
||||
collect(sourceAwait.createMessageReceipt(contact, true))
|
||||
if (sendAllPackets(bot, step, contact, packets)) {
|
||||
val sourceAwait = source?.await() ?: error("Internal error: source is not initialized")
|
||||
sourceAwait.tryEnsureSequenceIdAvailable()
|
||||
collect(sourceAwait.createMessageReceipt(contact, true))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true`, if source needs to be added
|
||||
*/
|
||||
private suspend fun OutgoingMessagePipelineContext.sendAllPackets(
|
||||
bot: AbstractBot,
|
||||
step: SendMessageStep,
|
||||
contact: Contact,
|
||||
packets: List<OutgoingPacket>
|
||||
) = packets.forEach { packet ->
|
||||
): Boolean {
|
||||
if (!step.allowMultiplePackets && packets.size != 1) {
|
||||
throw IllegalStateException("Internal error: step $step doesn't allow multiple packets while found ${packets.size} ones.")
|
||||
}
|
||||
|
||||
packets.forEach { packet ->
|
||||
if (!sendSinglePacket(bot, packet, step, contact)) return@sendAllPackets false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun OutgoingMessagePipelineContext.sendSinglePacket(
|
||||
bot: AbstractBot,
|
||||
packet: OutgoingPacket,
|
||||
step: SendMessageStep,
|
||||
contact: Contact,
|
||||
): Boolean {
|
||||
val originalMessage = attributes[ORIGINAL_MESSAGE]
|
||||
val protocolStrategy = components[MessageProtocolStrategy]
|
||||
val finalMessage = currentMessageChain
|
||||
|
||||
val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response
|
||||
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
|
||||
logger.info { "STEP $step: message too large." }
|
||||
val next = step.nextStepOrNull()
|
||||
?: throw MessageTooLargeException(
|
||||
contact,
|
||||
@ -88,13 +114,16 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
||||
)
|
||||
|
||||
// retry with next step
|
||||
processAlso(
|
||||
originalMessage.toMessageChain(),
|
||||
logger.info { "Retrying with STEP $next" }
|
||||
val (_, receipts) = processAlso(
|
||||
attributes[MESSAGE_TO_RETRY],
|
||||
extraAttributes = buildTypeSafeMap {
|
||||
set(STEP, next)
|
||||
},
|
||||
) // We expect to get a Receipt from processAlso
|
||||
return@forEach
|
||||
)
|
||||
check(receipts.size == 1) { "Internal error: expected exactly one receipt collected from sub-process, but found ${receipts.size}." }
|
||||
// We expect to get a Receipt from processAlso
|
||||
return false
|
||||
}
|
||||
if (resp is MessageSvcPbSendMsg.Response.ServiceUnavailable) {
|
||||
throw IllegalStateException("Send message to $contact failed, server service is unavailable.")
|
||||
@ -117,7 +146,7 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
||||
check(resp is MessageSvcPbSendMsg.Response.SUCCESS) {
|
||||
"Send message failed: $resp"
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||
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
|
||||
@ -45,24 +47,35 @@ internal class MusicShareProtocol : MessageProtocol() {
|
||||
}
|
||||
}
|
||||
|
||||
private class Sender : OutgoingMessageSender {
|
||||
open class Sender : OutgoingMessageSender {
|
||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||
val contact = attributes[CONTACT]
|
||||
val bot = contact.bot
|
||||
val musicShare = currentMessageChain[MusicShare] ?: return
|
||||
markAsConsumed()
|
||||
|
||||
val packet = MusicSharePacket(
|
||||
bot.client, musicShare, contact.id,
|
||||
targetKind = if (contact is Group) MessageSourceKind.GROUP else MessageSourceKind.FRIEND // always FRIEND
|
||||
)
|
||||
val result = bot.network.sendAndExpect(packet)
|
||||
result.pkg.checkSuccess("send music share")
|
||||
|
||||
val contact = attributes[CONTACT]
|
||||
val strategy = components[MessageProtocolStrategy]
|
||||
val bot = contact.bot
|
||||
|
||||
sendMusicSharePacket(bot, musicShare, contact, strategy)
|
||||
|
||||
val source = strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 3116)
|
||||
source.tryEnsureSequenceIdAvailable()
|
||||
|
||||
collect(source.createMessageReceipt(contact, true))
|
||||
}
|
||||
|
||||
protected open suspend fun sendMusicSharePacket(
|
||||
bot: QQAndroidBot,
|
||||
musicShare: MusicShare,
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>
|
||||
) {
|
||||
val packet = MusicSharePacket(
|
||||
bot.client, musicShare, contact.id,
|
||||
targetKind = if (contact is Group) MessageSourceKind.GROUP else MessageSourceKind.FRIEND // always FRIEND
|
||||
)
|
||||
val result = strategy.sendPacket(bot, packet)
|
||||
result.pkg.checkSuccess("send music share")
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.network.components.ClockHolder.Companion.clock
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageProcessor
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||
@ -34,6 +35,13 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
|
||||
return bot.network.sendAndExpect(packet)
|
||||
}
|
||||
|
||||
suspend fun <R : Packet?> sendPacket(bot: AbstractBot, packet: OutgoingPacketWithRespType<R>): R {
|
||||
return bot.network.sendAndExpect(packet)
|
||||
}
|
||||
|
||||
/**
|
||||
* If [fragmented] is false, returned list must contain at most one element.
|
||||
*/
|
||||
suspend fun createPacketsForGeneralMessage(
|
||||
client: QQAndroidClient,
|
||||
contact: C,
|
||||
|
@ -16,10 +16,7 @@ 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
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
||||
import net.mamoe.mirai.internal.pipeline.*
|
||||
import net.mamoe.mirai.internal.utils.estimateLength
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.*
|
||||
@ -38,12 +35,21 @@ internal interface OutgoingMessagePipeline :
|
||||
|
||||
internal open class OutgoingMessagePipelineImpl :
|
||||
AbstractProcessorPipeline<OutgoingMessagePipelineProcessor, OutgoingMessagePipelineContext, OutgoingMessagePipelineInput, MessageReceipt<*>>(
|
||||
PipelineConfiguration(stopWhenConsumed = false), @OptIn(TestOnly::class) defaultTraceLogging
|
||||
PipelineConfiguration(stopWhenConsumed = true), @OptIn(TestOnly::class) defaultTraceLogging
|
||||
), OutgoingMessagePipeline {
|
||||
|
||||
inner class OutgoingMessagePipelineContextImpl(
|
||||
attributes: TypeSafeMap, override var currentMessageChain: MessageChain
|
||||
) : OutgoingMessagePipelineContext, BaseContextImpl(attributes)
|
||||
) : OutgoingMessagePipelineContext, BaseContextImpl(attributes) {
|
||||
override suspend fun processAlso(
|
||||
data: OutgoingMessagePipelineInput,
|
||||
extraAttributes: TypeSafeMap
|
||||
): ProcessResult<out ProcessorPipelineContext<OutgoingMessagePipelineInput, MessageReceipt<*>>, MessageReceipt<*>> {
|
||||
return super.processAlso(data, extraAttributes).also { (context, _) ->
|
||||
this.currentMessageChain = (context as OutgoingMessagePipelineContext).currentMessageChain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createContext(
|
||||
data: OutgoingMessagePipelineInput, attributes: TypeSafeMap
|
||||
@ -61,6 +67,9 @@ internal open class OutgoingMessagePipelineImpl :
|
||||
|
||||
internal interface OutgoingMessagePipelineContext :
|
||||
ProcessorPipelineContext<OutgoingMessagePipelineInput, MessageReceipt<*>> {
|
||||
/**
|
||||
* Current message chain updated throughout the process. Will be updated from the [sub-processes][processAlso].
|
||||
*/
|
||||
var currentMessageChain: MessageChain
|
||||
|
||||
suspend fun MessageSource.tryEnsureSequenceIdAvailable() {
|
||||
@ -110,6 +119,10 @@ internal interface OutgoingMessagePipelineContext :
|
||||
*/
|
||||
val ORIGINAL_MESSAGE_AS_CHAIN = TypeKey<MessageChain>("originalMessageAsChain")
|
||||
|
||||
/**
|
||||
* Message chain used when retrying with next [step][SendMessageStep]s.
|
||||
*/
|
||||
val MESSAGE_TO_RETRY = TypeKey<MessageChain>("messageToRetry")
|
||||
|
||||
/**
|
||||
* Message target
|
||||
|
@ -49,6 +49,10 @@ internal interface MessageVisitorEx<in D, out R> : MessageVisitor<D, R> {
|
||||
return visitInternalFlagOnlyMessage(message, data)
|
||||
}
|
||||
|
||||
fun visitForceAsFragmentedMessage(message: ForceAsFragmentedMessage, data: D): R {
|
||||
return visitInternalFlagOnlyMessage(message, data)
|
||||
}
|
||||
|
||||
fun visitDontAsLongMessage(message: DontAsLongMessage, data: D): R {
|
||||
return visitInternalFlagOnlyMessage(message, data)
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushP
|
||||
import net.mamoe.mirai.internal.network.toPacket
|
||||
import net.mamoe.mirai.internal.pipeline.*
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
||||
import net.mamoe.mirai.utils.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ -90,17 +89,6 @@ internal open class NoticeProcessorPipelineImpl protected constructor(
|
||||
) : BaseContextImpl(attributes), NoticePipelineContext {
|
||||
override val bot: QQAndroidBot
|
||||
get() = this@NoticeProcessorPipelineImpl.bot
|
||||
|
||||
override suspend fun processAlso(
|
||||
data: ProtocolStruct,
|
||||
attributes: TypeSafeMap
|
||||
): ProcessResult<out ProcessorPipelineContext<ProtocolStruct, Packet>, Packet> {
|
||||
traceLogging.info { "processAlso: data=${data.structureToStringAndDesensitizeIfAvailable()}" }
|
||||
return process(data, this.attributes + attributes).also { packets ->
|
||||
this.collected.data += packets.collected
|
||||
traceLogging.info { "processAlso: result=$packets" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleExceptionInProcess(
|
||||
|
@ -16,6 +16,7 @@ import kotlinx.serialization.protobuf.ProtoType
|
||||
import net.mamoe.mirai.internal.utils.io.NestedStructure
|
||||
import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
|
||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.structureToStringIfAvailable
|
||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.utils.unzip
|
||||
@ -1005,11 +1006,18 @@ internal class ImMsgBody : ProtoBuf {
|
||||
@ProtoNumber(6) @JvmField val type: Int = 0,
|
||||
@ProtoNumber(7) @JvmField val richMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoNumber(8) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@NestedStructure(SrcMsgDesensitizer::class)
|
||||
@ProtoNumber(9) @JvmField val srcMsg: ByteArray? = null,
|
||||
@ProtoNumber(10) @JvmField val toUin: Long = 0L,
|
||||
@ProtoNumber(11) @JvmField val troopName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
) : ProtoBuf
|
||||
|
||||
internal object SrcMsgDesensitizer : NestedStructureDesensitizer<SourceMsg, MsgComm.Msg> {
|
||||
override fun deserialize(context: SourceMsg, byteArray: ByteArray): MsgComm.Msg {
|
||||
return byteArray.loadAs(MsgComm.Msg.serializer())
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal class Text(
|
||||
@ProtoNumber(1) @JvmField val str: String = "",
|
||||
|
@ -10,6 +10,7 @@
|
||||
package net.mamoe.mirai.internal.pipeline
|
||||
|
||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessor
|
||||
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
||||
import net.mamoe.mirai.utils.*
|
||||
@ -39,6 +40,7 @@ internal interface ProcessorPipeline<P : Processor<C, D>, C : ProcessorPipelineC
|
||||
|
||||
fun registerBefore(processor: P): DisposableRegistry
|
||||
|
||||
fun createContext(data: D, attributes: TypeSafeMap): C
|
||||
|
||||
/**
|
||||
* Process using the [context].
|
||||
@ -139,7 +141,7 @@ internal interface ProcessorPipelineContext<D, R> {
|
||||
annotation class ConsumptionMarker // to give an explicit color.
|
||||
|
||||
/**
|
||||
* Fire the [data] into the processor pipeline, and collect the results to current [collected].
|
||||
* Fire the [data] into the processor pipeline, and collect the results to current [collected], updating *some mutable properties* in contexts, e.g. [OutgoingMessagePipelineContext.currentMessageChain]
|
||||
*
|
||||
* @param extraAttributes extra attributes
|
||||
* @return result collected from processors. This would also have been collected to this context (where you call [processAlso]).
|
||||
@ -216,8 +218,6 @@ protected constructor(
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun createContext(data: D, attributes: TypeSafeMap): C
|
||||
|
||||
abstract inner class BaseContextImpl(
|
||||
attributes: TypeSafeMap,
|
||||
) : AbstractProcessorPipelineContext<D, R>(attributes, traceLogging) {
|
||||
@ -226,7 +226,10 @@ protected constructor(
|
||||
extraAttributes: TypeSafeMap
|
||||
): ProcessResult<out ProcessorPipelineContext<D, R>, R> {
|
||||
traceLogging.info { "processAlso: data=${data.structureToStringAndDesensitizeIfAvailable()}" }
|
||||
return process(data, this.attributes + extraAttributes).also {
|
||||
traceLogging.info { "extraAttributes = $extraAttributes" }
|
||||
val newAttributes = this.attributes + extraAttributes
|
||||
traceLogging.info { "newAttributes = $newAttributes" }
|
||||
return process(data, newAttributes).also {
|
||||
this.collected.data += it.collected
|
||||
traceLogging.info { "processAlso: result=$it" }
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ 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.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.AbstractBot
|
||||
import net.mamoe.mirai.internal.BotAccount
|
||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||
import net.mamoe.mirai.internal.message.data.inferMessageSourceKind
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
@ -41,6 +43,7 @@ import net.mamoe.mirai.internal.notice.processors.GroupExtensions
|
||||
import net.mamoe.mirai.internal.test.runBlockingUnit
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.Clock
|
||||
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
@ -52,9 +55,19 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.asserter
|
||||
|
||||
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions {
|
||||
init {
|
||||
System.setProperty("mirai.message.protocol.log.full", "true")
|
||||
System.setProperty("mirai.message.outgoing.pipeline.log.full", "true")
|
||||
}
|
||||
|
||||
override fun createAccount(): BotAccount = BotAccount(1230001L, "pwd")
|
||||
|
||||
protected abstract val protocols: Array<out MessageProtocol>
|
||||
protected var defaultTarget: ContactOrBot? = null
|
||||
protected var defaultTarget: ContactOrBot by lateinitMutableProperty {
|
||||
bot.addGroup(123, 1230003).apply {
|
||||
addMember(1230003, "user3", MemberPermission.OWNER)
|
||||
}
|
||||
}
|
||||
|
||||
private var decoderLoggerEnabled = false
|
||||
private var encoderLoggerEnabled = false
|
||||
@ -144,8 +157,8 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
var messages: MessageChainBuilder = MessageChainBuilder()
|
||||
|
||||
var groupIdOrZero: Long = 0
|
||||
var messageSourceKind: MessageSourceKind = MessageSourceKind.GROUP
|
||||
var target: ContactOrBot? = defaultTarget
|
||||
var target: ContactOrBot = defaultTarget
|
||||
var messageSourceKind: MessageSourceKind by lateinitMutableProperty { target.inferMessageSourceKind() }
|
||||
var withGeneralFlags = true
|
||||
var isForward = false
|
||||
|
||||
@ -157,12 +170,10 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
messages.addAll(message)
|
||||
}
|
||||
|
||||
fun target(target: ContactOrBot?) {
|
||||
fun target(target: ContactOrBot) {
|
||||
this.target = target
|
||||
|
||||
if (target != null) {
|
||||
messageSourceKind = target.inferMessageSourceKind()
|
||||
}
|
||||
messageSourceKind = target.inferMessageSourceKind()
|
||||
|
||||
if (target is Group) {
|
||||
groupIdOrZero = target.id
|
||||
@ -232,51 +243,53 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
open inner class TestMessageProtocolStrategy : 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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
components[MessageProtocolStrategy] = TestMessageProtocolStrategy()
|
||||
components[HighwayUploader] = object : HighwayUploader {
|
||||
override suspend fun uploadMessages(
|
||||
contact: AbstractContact,
|
||||
@ -300,7 +313,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
fun runWithFacade(action: suspend MessageProtocolFacade.() -> Unit) {
|
||||
runBlockingUnit {
|
||||
facadeOf(*protocols).run { action() }
|
||||
MessageProtocolFacade.INSTANCE.run { action() }
|
||||
MessageProtocolFacade.INSTANCE.copy().run { action() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.ForwardMessageInternal
|
||||
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.cast
|
||||
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 ForwardMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
override val protocols: Array<out MessageProtocol> =
|
||||
arrayOf(
|
||||
TextProtocol(),
|
||||
ImageProtocol(),
|
||||
ForwardMessageProtocol(),
|
||||
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 ForwardMessage to ForwardMessageInternal`() {
|
||||
var message = buildForwardMessage(defaultTarget.cast()) {
|
||||
currentTime = 16000000
|
||||
1 named "Name1" says "Hello"
|
||||
}.toMessageChain()
|
||||
|
||||
message += IgnoreLengthCheck
|
||||
|
||||
runWithFacade {
|
||||
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
|
||||
val receipt = receipts.single()
|
||||
assertMessageEquals(message.dropMiraiInternalFlags(), receipt.source.originalMessage)
|
||||
|
||||
assertMessageEquals(
|
||||
ForwardMessageInternal(
|
||||
"""<?xml version="1.0" encoding="utf-8"?> <msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]" m_resid="(size=1)501389E3070B20D87A80A67961F4EA0E" m_fileName="160023" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"> <item layout="1" advertiser_id="0" aid="0"> <title size="34" maxLines="2" lineSpace="12">群聊的聊天记录</title> <title size="26" color="#777777" maxLines="2" lineSpace="12">Name1: Hello</title> <hr hidden="false" style="0"/> <summary size="26" color="#777777">查看1条转发消息</summary> </item> <source name="聊天记录" icon="" action="" appid="-1"/> </msg>""",
|
||||
"(size=1)501389E3070B20D87A80A67961F4EA0E",
|
||||
null,
|
||||
origin = message[ForwardMessage]
|
||||
) + IgnoreLengthCheck, context.currentMessageChain
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can convert empty ForwardMessage`() {
|
||||
val message = buildForwardMessage(defaultTarget.cast()) {}.toMessageChain()
|
||||
|
||||
runWithFacade {
|
||||
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
|
||||
val receipt = receipts.single()
|
||||
assertMessageEquals(message.dropMiraiInternalFlags(), receipt.source.originalMessage)
|
||||
|
||||
assertMessageEquals(
|
||||
ForwardMessageInternal(
|
||||
"""<?xml version="1.0" encoding="utf-8"?> <msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]" m_resid="(size=0)D41D8CD98F00B204E9800998ECF8427E" m_fileName="160023" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"> <item layout="1" advertiser_id="0" aid="0"> <title size="34" maxLines="2" lineSpace="12">群聊的聊天记录</title> <hr hidden="false" style="0"/> <summary size="26" color="#777777">查看0条转发消息</summary> </item> <source name="聊天记录" icon="" action="" appid="-1"/> </msg>""",
|
||||
"(size=0)D41D8CD98F00B204E9800998ECF8427E",
|
||||
null,
|
||||
origin = message[ForwardMessage]
|
||||
), context.currentMessageChain
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO: 2022/5/23 test for download ForwardMessage
|
||||
// @Test
|
||||
// fun `can receive and download ForwardMessage`() {
|
||||
// val message = runBlocking {
|
||||
// runWithFacade {
|
||||
// net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
|
||||
// msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
|
||||
// fromUin = 1230001,
|
||||
// toUin = 1230002,
|
||||
// msgType = 166,
|
||||
// c2cCmd = 11,
|
||||
// msgSeq = 34437,
|
||||
// msgTime = 1653334690,
|
||||
// msgUid = 72057594524997436,
|
||||
// wseqInC2cMsghead = 34437,
|
||||
// ),
|
||||
// msgBody = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MsgBody(
|
||||
// richText = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichText(
|
||||
// attr = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Attr(
|
||||
// codePage = 0,
|
||||
// time = 1653334690,
|
||||
// random = 487069500,
|
||||
// effect = 0,
|
||||
// charSet = 134,
|
||||
// pitchAndFamily = 2,
|
||||
// fontName = "宋体",
|
||||
// ),
|
||||
// elems = mutableListOf(
|
||||
// net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
// richMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichMsg(
|
||||
// template1 = "01 78 9C B5 92 4F 6F D3 30 18 C6 BF 8A 65 0E 39 4C 23 4D FA 2F A0 38 53 B7 29 EB D8 12 89 65 45 FC 11 9A DC C4 C9 2C D9 49 65 3B DD DA DB 24 0E 08 0E 88 03 17 04 42 48 70 40 42 C0 89 DB 3E 4E BB 7E 0C 1C 77 12 E3 80 C4 85 F8 10 47 AF F3 F3 F3 3C EF EB 6F 9D 73 06 A6 44 48 5A 95 C8 72 6E B7 2C 40 CA B4 CA 68 59 20 6B 74 1C 6E 7A 16 90 0A 97 19 66 55 49 90 35 23 D2 02 5B 81 CF 65 01 24 11 53 9A 92 FD 5D 04 DB 5D 08 14 E1 13 86 95 F9 76 20 C0 A9 6A 98 70 4A C9 59 54 33 45 23 59 40 30 16 94 E4 08 3E 59 5D BC 58 7C FA B2 FA F6 63 71 F9 E6 29 04 FC 44 10 49 33 04 C7 C7 E1 91 3C 4C F2 78 F4 28 89 C2 68 7B 63 E0 B9 03 B1 71 FF E1 78 87 1E A4 C5 38 8E 6B 6F 6F BB FF 60 CF 3E 9A 77 0E 1E C7 C3 D1 4E 31 BC 37 8F C2 DD 3C 12 83 44 34 A8 9C 32 12 63 4E 10 EC 3B 2D BD BC 8E 73 C7 E9 BA 1D A7 D7 ED 35 3A 93 9A 23 E8 42 20 AB 5A A4 44 CB DA D7 17 B7 20 A8 05 43 10 82 9C E1 42 3B D2 0E 32 1D 4C 42 8B D2 54 F9 B5 87 D0 94 5B 30 F0 A9 76 0C 18 9E 55 B5 5A 3B 6E CE 2B AA 63 39 A1 6B 22 5E BF 03 5F 51 C5 08 90 74 AE 45 B5 3B 1A 86 CF 0F 69 49 A4 D1 C1 F4 2E 99 E0 54 D7 1C 17 06 43 CA 1D CF E3 15 B9 7A FB EC 66 4C BE 6D 28 7F C2 DC 1E 04 69 C5 2A 81 E0 AD BE 79 FE 11 7E 17 80 FC 2C A3 ED FF 4C 3D 15 E0 94 66 19 D1 19 E6 98 49 A2 53 57 33 46 4C 3A 76 E0 CB 9A 73 2C 66 7F BF 36 58 7E F8 7C F5 EE A5 BB 7C FF 71 75 F9 75 F1 EA F5 F2 E7 F3 E5 C5 77 DF BE FE 33 F0 ED A6 0D 9A 64 9A 09 4A D3 F7 9B B1 41 40 D3 66 0E 7F 4F A4 DE 4D 26 4D 67 36 1D 23 C2 D6 C3 1C FC 02 21 C3 0A 7E".hexToBytes(),
|
||||
// serviceId = 35,
|
||||
// ),
|
||||
// ),
|
||||
// net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
// generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
// pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 CA 04 00 D2 05 02 08 6A".hexToBytes(),
|
||||
// ),
|
||||
// ),
|
||||
// net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ).toMessageChainOnline(bot, facade = this)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 kotlinx.coroutines.Deferred
|
||||
import net.mamoe.mirai.internal.AbstractBot
|
||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.dropMiraiInternalFlags
|
||||
import net.mamoe.mirai.internal.message.flags.ForceAsFragmentedMessage
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import net.mamoe.mirai.message.data.messageChainOf
|
||||
import net.mamoe.mirai.utils.castUp
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
internal class GeneralMessageSenderProtocolTest : AbstractMessageProtocolTest() {
|
||||
override val protocols: Array<out MessageProtocol> = arrayOf(TextProtocol(), GeneralMessageSenderProtocol())
|
||||
|
||||
@Test
|
||||
fun `can convert messages failed to send to fragmented`() {
|
||||
val message = messageChainOf(PlainText("test"), PlainText("test"))
|
||||
|
||||
runWithFacade {
|
||||
components[MessageProtocolStrategy] = object : TestMessageProtocolStrategy() {
|
||||
var count = 0
|
||||
override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet {
|
||||
println("MessageProtocolStrategy.sendPacket called: $count")
|
||||
if (count++ <= 1) { // fail the first and second attempt
|
||||
return MessageSvcPbSendMsg.Response.MessageTooLarge
|
||||
}
|
||||
return super.sendPacket(bot, packet)
|
||||
}
|
||||
|
||||
override suspend fun createPacketsForGeneralMessage(
|
||||
client: QQAndroidClient,
|
||||
contact: AbstractContact,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): List<OutgoingPacket> {
|
||||
if (count == 2) {
|
||||
assertTrue { fragmented }
|
||||
} else {
|
||||
assertFalse { fragmented }
|
||||
}
|
||||
return super.createPacketsForGeneralMessage(
|
||||
client,
|
||||
contact,
|
||||
message,
|
||||
originalMessage,
|
||||
fragmented,
|
||||
sourceCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
|
||||
val receipt = receipts.single()
|
||||
assertMessageEquals(message.dropMiraiInternalFlags(), receipt.source.originalMessage)
|
||||
assertMessageEquals(message.dropMiraiInternalFlags(), context.currentMessageChain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can convert messages to fragmented`() {
|
||||
val message = messageChainOf(PlainText("test"), PlainText("test"), ForceAsFragmentedMessage)
|
||||
|
||||
runWithFacade {
|
||||
components[MessageProtocolStrategy] = object : TestMessageProtocolStrategy() {
|
||||
override suspend fun createPacketsForGeneralMessage(
|
||||
client: QQAndroidClient,
|
||||
contact: AbstractContact,
|
||||
message: MessageChain,
|
||||
originalMessage: MessageChain,
|
||||
fragmented: Boolean,
|
||||
sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit
|
||||
): List<OutgoingPacket> {
|
||||
assertTrue { fragmented }
|
||||
return super.createPacketsForGeneralMessage(
|
||||
client,
|
||||
contact,
|
||||
message,
|
||||
originalMessage,
|
||||
fragmented,
|
||||
sourceCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
|
||||
val receipt = receipts.single()
|
||||
assertMessageEquals(message.dropMiraiInternalFlags(), receipt.source.originalMessage)
|
||||
assertMessageEquals(message, context.currentMessageChain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -10,13 +10,19 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.AbstractBot
|
||||
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.internal.message.protocol.outgoing.MessageProtocolStrategy
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.repeat
|
||||
import net.mamoe.mirai.message.data.toMessageChain
|
||||
import net.mamoe.mirai.message.data.toPlainText
|
||||
import net.mamoe.mirai.utils.castUp
|
||||
import net.mamoe.mirai.utils.getRandomString
|
||||
@ -86,4 +92,46 @@ internal class LongMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can convert messages failed to send at FIRST step to LongMessageInternal`() {
|
||||
val message = "test".toPlainText().toMessageChain()
|
||||
|
||||
runWithFacade {
|
||||
components[MessageProtocolStrategy] = object : TestMessageProtocolStrategy() {
|
||||
var count = 0
|
||||
override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet {
|
||||
println("MessageProtocolStrategy.sendPacket called: $count")
|
||||
if (count++ == 0) { // fail the first attempt
|
||||
return MessageSvcPbSendMsg.Response.MessageTooLarge
|
||||
}
|
||||
return super.sendPacket(bot, packet)
|
||||
}
|
||||
}
|
||||
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="test"
|
||||
m_resid="(size=1)8698F15C27DA63648CEF9A93EA76B084"
|
||||
m_fileName="160023" sourceMsgId="0" url=""
|
||||
flag="3" adverSign="0" multiMsgFlag="1">
|
||||
<item layout="1">
|
||||
<title>test</title>
|
||||
<hr hidden="false" style="0"/>
|
||||
<summary>点击查看完整消息</summary>
|
||||
</item>
|
||||
<source name="聊天记录" icon="" action="" appid="-1"/>
|
||||
</msg>
|
||||
""".trimIndent(), "(size=1)8698F15C27DA63648CEF9A93EA76B084"
|
||||
), context.currentMessageChain
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,19 +10,26 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageProcessorAdapter
|
||||
import net.mamoe.mirai.internal.pipeline.replaceProcessor
|
||||
import net.mamoe.mirai.message.data.LightApp
|
||||
import net.mamoe.mirai.message.data.MessageOrigin
|
||||
import net.mamoe.mirai.message.data.MessageOriginKind
|
||||
import net.mamoe.mirai.message.data.MusicKind.NeteaseCloudMusic
|
||||
import net.mamoe.mirai.message.data.MusicShare
|
||||
import net.mamoe.mirai.utils.castUp
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
||||
override val protocols: Array<out MessageProtocol> =
|
||||
arrayOf(TextProtocol(), MusicShareProtocol(), RichMessageProtocol())
|
||||
arrayOf(TextProtocol(), MusicShareProtocol(), RichMessageProtocol(), GeneralMessageSenderProtocol())
|
||||
|
||||
@BeforeEach
|
||||
fun `init group`() {
|
||||
@ -73,5 +80,40 @@ internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
// no encoder. specially handled, no test for now.
|
||||
@Test
|
||||
fun `can send MusicShare to group`() {
|
||||
val message = MusicShare(
|
||||
kind = NeteaseCloudMusic,
|
||||
title = "ジェリーフィッシュ",
|
||||
summary = "Yunomi/ローラーガール",
|
||||
jumpUrl = "https://y.music.163.com/m/song?id=562591636&uct=QK0IOc%2FSCIO8gBNG%2Bwcbsg%3D%3D&app_version=8.7.46",
|
||||
pictureUrl = "http://p1.music.126.net/KaYSb9oYQzhl2XBeJcj8Rg==/109951165125601702.jpg",
|
||||
musicUrl = "http://music.163.com/song/media/outer/url?id=562591636&userid=324076307&sc=wmv&tn=",
|
||||
brief = "[分享]ジェリーフィッシュ",
|
||||
)
|
||||
|
||||
runWithFacade {
|
||||
assertTrue {
|
||||
outgoingPipeline.replaceProcessor(
|
||||
{ it is MusicShareProtocol.Sender },
|
||||
OutgoingMessageProcessorAdapter(object : MusicShareProtocol.Sender() {
|
||||
override suspend fun sendMusicSharePacket(
|
||||
bot: QQAndroidBot,
|
||||
musicShare: MusicShare,
|
||||
contact: AbstractContact,
|
||||
strategy: MessageProtocolStrategy<*>
|
||||
) {
|
||||
// nop
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
preprocessAndSendOutgoingImpl(defaultTarget.castUp(), message, components).let { (context, receipts) ->
|
||||
val receipt = receipts.single()
|
||||
assertMessageEquals(message, receipt.source.originalMessage)
|
||||
assertMessageEquals(message, context.currentMessageChain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,48 +7,16 @@
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.decodeAndRefineLight
|
||||
import net.mamoe.mirai.message.data.Face
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import net.mamoe.mirai.message.data.QuoteReply
|
||||
import net.mamoe.mirai.message.data.messageChainOf
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import org.junit.jupiter.api.Test
|
||||
@ -57,8 +25,9 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
||||
override val protocols: Array<out MessageProtocol> = arrayOf(QuoteReplyProtocol(), TextProtocol())
|
||||
|
||||
@Test
|
||||
fun `decode group reference group`() {
|
||||
fun `decode referencing online group message in group`() {
|
||||
buildCodingChecks {
|
||||
targetGroup()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
@ -82,38 +51,388 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
||||
),
|
||||
),
|
||||
)
|
||||
// message(
|
||||
// QuoteReply(
|
||||
//// OfflineMessageSourceImplData(
|
||||
////
|
||||
//// )
|
||||
// )
|
||||
// )
|
||||
targetGroup()
|
||||
useOrdinaryEquality()
|
||||
message(
|
||||
QuoteReply(
|
||||
OfflineMessageSourceImplData(
|
||||
ids = intArrayOf(1803),
|
||||
internalIds = intArrayOf(539443883),
|
||||
time = 1653147259,
|
||||
originalMessage = messageChainOf(PlainText("a")),
|
||||
kind = messageSourceKind,
|
||||
fromId = 1230001,
|
||||
targetId = 1,
|
||||
botId = bot.id,
|
||||
)
|
||||
), PlainText("s")
|
||||
)
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can decode`() {
|
||||
doDecoderChecks(
|
||||
messageChainOf(Face(Face.YIN_XIAN)),
|
||||
) {
|
||||
decodeAndRefineLight(
|
||||
listOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
face = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Face(
|
||||
index = 108,
|
||||
old = "14 AD".hexToBytes(),
|
||||
fun `encode referencing offline group message in group`() {
|
||||
buildCodingChecks {
|
||||
targetGroup()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(-31257),
|
||||
senderUin = 1230001,
|
||||
time = 1653326514,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
// mirai's OfflineMessageSource has no enough information to create 'srcMsg'
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
),
|
||||
groupIdOrZero = 0,
|
||||
MessageSourceKind.GROUP,
|
||||
bot,
|
||||
)
|
||||
}
|
||||
message(
|
||||
QuoteReply(
|
||||
OfflineMessageSourceImplData(
|
||||
ids = intArrayOf(-31257),
|
||||
internalIds = intArrayOf(1860746670),
|
||||
time = 1653326514,
|
||||
originalMessage = messageChainOf(PlainText("a")),
|
||||
kind = messageSourceKind,
|
||||
fromId = 1230001,
|
||||
targetId = 1994701021,
|
||||
botId = bot.id,
|
||||
)
|
||||
), PlainText("s")
|
||||
)
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
|
||||
private val onlineIncomingGroupMessage = runCoroutineInPlace {
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
|
||||
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
|
||||
fromUin = 1230001,
|
||||
toUin = 1230002,
|
||||
msgType = 166,
|
||||
c2cCmd = 11,
|
||||
msgSeq = 31245,
|
||||
msgTime = 1653330864,
|
||||
msgUid = 72057594652150074,
|
||||
wseqInC2cMsghead = 31245,
|
||||
),
|
||||
msgBody = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MsgBody(
|
||||
richText = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichText(
|
||||
attr = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Attr(
|
||||
codePage = 0,
|
||||
time = 1653330864,
|
||||
random = 614222138,
|
||||
size = 9,
|
||||
effect = 0,
|
||||
charSet = 134,
|
||||
pitchAndFamily = 0,
|
||||
fontName = "Helvetica",
|
||||
),
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 61".hexToBytes(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).toMessageChainOnline(bot)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `encode referencing online incoming group message in group`() {
|
||||
buildCodingChecks {
|
||||
targetGroup()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(31245),
|
||||
senderUin = 1230001,
|
||||
time = 1653330864,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 61".hexToBytes(),
|
||||
),
|
||||
),
|
||||
),
|
||||
pbReserve = "18 BA 92 F1 A4 02".hexToBytes(),
|
||||
srcMsg = "0A 20 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 8D F4 01 30 B0 A7 AF 94 06 38 BA 92 F1 A4 02 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 61 12 02 4A 00".hexToBytes(),
|
||||
toUin = 1230002,
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
message(onlineIncomingGroupMessage.quote(), PlainText("s"))
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
// stranger and group temp are almost the same for friend.
|
||||
@Test
|
||||
fun `decode referencing online incoming private message in friend`() {
|
||||
buildCodingChecks {
|
||||
targetFriend()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(34279),
|
||||
senderUin = 1230001,
|
||||
time = 1653326514,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
),
|
||||
pbReserve = "18 AE FB A2 F7 86 80 80 80 01".hexToBytes(),
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 51".hexToBytes(),
|
||||
),
|
||||
),
|
||||
)
|
||||
message(
|
||||
QuoteReply(
|
||||
OfflineMessageSourceImplData(
|
||||
ids = intArrayOf(34279),
|
||||
internalIds = intArrayOf(1860746670),
|
||||
time = 1653326514,
|
||||
originalMessage = messageChainOf(PlainText("a")),
|
||||
kind = messageSourceKind,
|
||||
fromId = 1230001,
|
||||
targetId = 0, // the referenced message was actually sending from friend 1230001 to bot.
|
||||
botId = bot.id,
|
||||
)
|
||||
), PlainText("s")
|
||||
)
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decode referencing online outgoing private message in friend`() {
|
||||
buildCodingChecks {
|
||||
targetFriend()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(49858),
|
||||
senderUin = 1230002,
|
||||
time = 1653329998,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "b",
|
||||
),
|
||||
),
|
||||
),
|
||||
pbReserve = "18 C3 94 C4 B3 84 80 80 80 01".hexToBytes(),
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 5E".hexToBytes(),
|
||||
),
|
||||
),
|
||||
)
|
||||
message(
|
||||
QuoteReply(
|
||||
OfflineMessageSourceImplData(
|
||||
ids = intArrayOf(49858),
|
||||
internalIds = intArrayOf(1181813315),
|
||||
time = 1653329998,
|
||||
originalMessage = messageChainOf(PlainText("b")),
|
||||
kind = messageSourceKind,
|
||||
fromId = 1230002, // bot id
|
||||
targetId = 0, // the referenced message was actually sending from bot to the friend 1230001.
|
||||
botId = bot.id,
|
||||
)
|
||||
), PlainText("s")
|
||||
)
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `encode referencing offline private message in friend`() {
|
||||
buildCodingChecks {
|
||||
targetFriend()
|
||||
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(-31257),
|
||||
senderUin = 1230001,
|
||||
time = 1653326514,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
),
|
||||
// mirai's OfflineMessageSource has no enough information to create 'srcMsg'
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
),
|
||||
)
|
||||
message(
|
||||
QuoteReply(
|
||||
OfflineMessageSourceImplData(
|
||||
ids = intArrayOf(-31257),
|
||||
internalIds = intArrayOf(1860746670),
|
||||
time = 1653326514,
|
||||
originalMessage = messageChainOf(PlainText("a")),
|
||||
kind = messageSourceKind,
|
||||
fromId = 1230001,
|
||||
targetId = 1994701021,
|
||||
botId = bot.id,
|
||||
)
|
||||
), PlainText("s")
|
||||
)
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
init {
|
||||
bot.addFriend(1230001)
|
||||
}
|
||||
|
||||
private val onlineIncomingFriendMessage: MessageChain = runCoroutineInPlace {
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
|
||||
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
|
||||
fromUin = 1230001,
|
||||
toUin = 1230002,
|
||||
msgType = 166,
|
||||
c2cCmd = 11,
|
||||
msgSeq = 31222,
|
||||
msgTime = 1653328003,
|
||||
msgUid = 72057595832827069,
|
||||
wseqInC2cMsghead = 31222,
|
||||
),
|
||||
msgBody = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MsgBody(
|
||||
richText = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichText(
|
||||
attr = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Attr(
|
||||
codePage = 0,
|
||||
time = 1653328002,
|
||||
random = 1794899133,
|
||||
size = 9,
|
||||
effect = 0,
|
||||
charSet = 134,
|
||||
pitchAndFamily = 0,
|
||||
fontName = "Helvetica",
|
||||
),
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 4F".hexToBytes(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).toMessageChainOnline(bot)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `encode referencing online incoming private message in friend`() {
|
||||
buildCodingChecks {
|
||||
targetFriend()
|
||||
elem(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||
origSeqs = intArrayOf(31222),
|
||||
senderUin = 1230001,
|
||||
time = 1653328003,
|
||||
flag = 1,
|
||||
elems = mutableListOf(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "a",
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
), // Don't worry about this empty Elem, it's from official
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
|
||||
pbReserve = "78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 4F".hexToBytes(),
|
||||
),
|
||||
),
|
||||
),
|
||||
pbReserve = "18 BD F9 EF D7 06".hexToBytes(),
|
||||
// srcMsg is available for online source
|
||||
srcMsg = "0A 20 08 B1 89 4B 10 B2 89 4B 18 A6 01 20 0B 28 F6 F3 01 30 83 91 AF 94 06 38 BD F9 EF D7 06 E0 01 01 1A 2D 0A 2B 12 05 0A 03 0A 01 61 12 00 12 1C AA 02 19 9A 01 16 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 CA 04 00 D2 05 02 08 4F 12 02 4A 00".hexToBytes(),
|
||||
toUin = 1230002,
|
||||
),
|
||||
),
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||
str = "s",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
message(onlineIncomingFriendMessage.quote(), PlainText("s"))
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
private fun CodingChecksBuilder.targetGroup() {
|
||||
@ -121,7 +440,7 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
|
||||
private fun CodingChecksBuilder.targetFriend() {
|
||||
target(bot.addFriend(1))
|
||||
target(bot.getFriendOrFail(1230001))
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.network.framework
|
||||
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.internal.BotAccount
|
||||
import net.mamoe.mirai.internal.MockBot
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
|
||||
@ -31,6 +32,8 @@ import net.mamoe.mirai.internal.utils.subLogger
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import network.framework.components.TestEventDispatcherImpl
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
@ -41,9 +44,13 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT
|
||||
protected open fun createNetworkHandlerContext() = TestNetworkHandlerContext(bot, logger, components)
|
||||
protected open fun createNetworkHandler() = TestNetworkHandler(bot, createNetworkHandlerContext())
|
||||
|
||||
protected val bot: QQAndroidBot = MockBot {
|
||||
nhProvider = { createNetworkHandler() }
|
||||
additionalComponentsProvider = { this@AbstractMockNetworkHandlerTest.components }
|
||||
protected open fun createAccount() = BotAccount(Random.nextLong().absoluteValue.mod(1000L), "pwd")
|
||||
|
||||
protected val bot: QQAndroidBot by lazy {
|
||||
MockBot(createAccount()) {
|
||||
nhProvider = { createNetworkHandler() }
|
||||
additionalComponentsProvider = { this@AbstractMockNetworkHandlerTest.components }
|
||||
}
|
||||
}
|
||||
protected val logger = MiraiLogger.Factory.create(Bot::class, "test")
|
||||
protected val components = ConcurrentComponentStorage().apply {
|
||||
|
@ -37,11 +37,8 @@ import kotlin.test.assertTrue
|
||||
internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
||||
private val bot = MockBot()
|
||||
|
||||
/**
|
||||
* This test is very ugly, but we cannot do anything else.
|
||||
*/ // We need #1304
|
||||
@Test
|
||||
fun `refine ForwardMessageInternal for MessageReceipt`() = runBlockingUnit {
|
||||
fun `refine ForwardMessageInternal for MessageReceipt's original MessageChain`() = runBlockingUnit {
|
||||
val group = bot.addGroup(123, 2)
|
||||
|
||||
val forward = buildForwardMessage(group) {
|
||||
@ -52,7 +49,7 @@ internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
||||
val facade = MessageProtocolFacade.INSTANCE.copy()
|
||||
|
||||
assertTrue {
|
||||
facade.preprocessorPipeline.replaceProcessor(
|
||||
facade.outgoingPipeline.replaceProcessor(
|
||||
{ it is GeneralMessageSenderProtocol.GeneralMessageSender },
|
||||
OutgoingMessageProcessorAdapter(object : OutgoingMessageSender {
|
||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||
|
@ -18,9 +18,9 @@ import net.mamoe.mirai.internal.MockBot
|
||||
import net.mamoe.mirai.internal.getMiraiImpl
|
||||
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
|
||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer
|
||||
import net.mamoe.mirai.internal.message.RefinableMessage
|
||||
import net.mamoe.mirai.internal.message.RefineContext
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
@ -326,7 +326,7 @@ private fun sourceStub(
|
||||
|
||||
private suspend fun testRecursiveRefine(list: List<ImMsgBody.Elem>, expected: MessageChain, isLight: Boolean) {
|
||||
val actual = buildMessageChain {
|
||||
ReceiveMessageTransformer.joinToMessageChain(list, 0, MessageSourceKind.GROUP, bot, this)
|
||||
MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, bot, this, null)
|
||||
}.let { c ->
|
||||
if (isLight) {
|
||||
c.refineLight(bot)
|
||||
|
Loading…
Reference in New Issue
Block a user