mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-05 00:10: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 {
|
override fun nextStepOrNull(): SendMessageStep {
|
||||||
return LONG_MESSAGE
|
return LONG_MESSAGE
|
||||||
}
|
}
|
||||||
@ -57,7 +59,7 @@ internal enum class SendMessageStep {
|
|||||||
/**
|
/**
|
||||||
* 尝试通过长消息通道上传长消息取得 resId 后再通过普通消息通道发送长消息标识
|
* 尝试通过长消息通道上传长消息取得 resId 后再通过普通消息通道发送长消息标识
|
||||||
*/
|
*/
|
||||||
LONG_MESSAGE {
|
LONG_MESSAGE(false) {
|
||||||
override fun nextStepOrNull(): SendMessageStep {
|
override fun nextStepOrNull(): SendMessageStep {
|
||||||
return FRAGMENTED
|
return FRAGMENTED
|
||||||
}
|
}
|
||||||
@ -66,7 +68,7 @@ internal enum class SendMessageStep {
|
|||||||
/**
|
/**
|
||||||
* 发送分片多包发送
|
* 发送分片多包发送
|
||||||
*/
|
*/
|
||||||
FRAGMENTED {
|
FRAGMENTED(true) {
|
||||||
override fun nextStepOrNull(): SendMessageStep? {
|
override fun nextStepOrNull(): SendMessageStep? {
|
||||||
return null
|
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.DeepMessageRefiner.refineDeep
|
||||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
||||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
|
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.ReceiveMessageTransformer.toAudio
|
||||||
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
||||||
import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
|
import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
|
||||||
@ -34,10 +33,11 @@ internal fun ImMsgBody.SourceMsg.toMessageChainNoSource(
|
|||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
groupIdOrZero: Long,
|
groupIdOrZero: Long,
|
||||||
refineContext: RefineContext = EmptyRefineContext,
|
refineContext: RefineContext = EmptyRefineContext,
|
||||||
|
facade: MessageProtocolFacade = MessageProtocolFacade
|
||||||
): MessageChain {
|
): MessageChain {
|
||||||
val elements = this.elems
|
val elements = this.elems
|
||||||
return buildMessageChain(elements.size + 1) {
|
return buildMessageChain(elements.size + 1) {
|
||||||
joinToMessageChain(elements, groupIdOrZero, messageSourceKind, bot, this)
|
facade.decode(elements, groupIdOrZero, messageSourceKind, bot, this, null)
|
||||||
}.cleanupRubbishMessageElements().refineLight(bot, refineContext)
|
}.cleanupRubbishMessageElements().refineLight(bot, refineContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,13 +47,15 @@ internal suspend fun List<MsgComm.Msg>.toMessageChainOnline(
|
|||||||
groupIdOrZero: Long,
|
groupIdOrZero: Long,
|
||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
refineContext: RefineContext = EmptyRefineContext,
|
refineContext: RefineContext = EmptyRefineContext,
|
||||||
|
facade: MessageProtocolFacade = MessageProtocolFacade
|
||||||
): MessageChain {
|
): 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(
|
internal suspend fun MsgComm.Msg.toMessageChainOnline(
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
refineContext: RefineContext = EmptyRefineContext,
|
refineContext: RefineContext = EmptyRefineContext,
|
||||||
|
facade: MessageProtocolFacade = MessageProtocolFacade,
|
||||||
): MessageChain {
|
): MessageChain {
|
||||||
fun getSourceKind(c2cCmd: Int): MessageSourceKind {
|
fun getSourceKind(c2cCmd: Int): MessageSourceKind {
|
||||||
return when (c2cCmd) {
|
return when (c2cCmd) {
|
||||||
@ -69,7 +71,7 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline(
|
|||||||
MessageSourceKind.GROUP -> msgHead.groupInfo?.groupCode ?: 0
|
MessageSourceKind.GROUP -> msgHead.groupInfo?.groupCode ?: 0
|
||||||
else -> 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(
|
//internal fun List<MsgComm.Msg>.toMessageChainOffline(
|
||||||
@ -95,20 +97,21 @@ private fun List<MsgComm.Msg>.toMessageChain(
|
|||||||
groupIdOrZero: Long,
|
groupIdOrZero: Long,
|
||||||
onlineSource: Boolean?,
|
onlineSource: Boolean?,
|
||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
|
facade: MessageProtocolFacade = MessageProtocolFacade,
|
||||||
): MessageChain {
|
): MessageChain {
|
||||||
val messageList = this
|
val messageList = this
|
||||||
|
|
||||||
|
|
||||||
val elements = messageList.flatMap { it.msgBody.richText.elems }
|
val builder = MessageChainBuilder(messageList.sumOf { it.msgBody.richText.elems.size })
|
||||||
|
|
||||||
val builder = MessageChainBuilder(elements.size)
|
|
||||||
|
|
||||||
if (onlineSource != null) {
|
if (onlineSource != null) {
|
||||||
builder.add(ReceiveMessageTransformer.createMessageSource(bot, onlineSource, messageSourceKind, messageList))
|
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) {
|
for (msg in messageList) {
|
||||||
msg.msgBody.richText.ptt?.toAudio()?.let { builder.add(it) }
|
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() {
|
fun MessageChainBuilder.compressContinuousPlainText() {
|
||||||
var index = 0
|
var index = 0
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
|
@ -12,10 +12,7 @@
|
|||||||
package net.mamoe.mirai.internal.message.flags
|
package net.mamoe.mirai.internal.message.flags
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.visitor.ex
|
import net.mamoe.mirai.internal.message.visitor.ex
|
||||||
import net.mamoe.mirai.message.data.AbstractMessageKey
|
import net.mamoe.mirai.message.data.*
|
||||||
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.visitor.MessageVisitor
|
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||||
import net.mamoe.mirai.utils.safeCast
|
import net.mamoe.mirai.utils.safeCast
|
||||||
|
|
||||||
@ -24,17 +21,35 @@ import net.mamoe.mirai.utils.safeCast
|
|||||||
*/
|
*/
|
||||||
internal sealed interface InternalFlagOnlyMessage : MessageMetadata
|
internal sealed interface InternalFlagOnlyMessage : MessageMetadata
|
||||||
|
|
||||||
|
internal sealed interface ForceAs : InternalFlagOnlyMessage, ConstrainSingle {
|
||||||
|
companion object Key : AbstractMessageKey<ForceAs>({ it.safeCast() })
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部 flag, 放入 chain 强制作为 long 发送
|
* 内部 flag, 放入 chain 强制作为 long 发送
|
||||||
*/
|
*/
|
||||||
internal object ForceAsLongMessage : MessageMetadata, ConstrainSingle, InternalFlagOnlyMessage,
|
internal object ForceAsLongMessage : ForceAs,
|
||||||
AbstractMessageKey<ForceAsLongMessage>({ it.safeCast() }) {
|
AbstractPolymorphicMessageKey<ForceAs, ForceAsLongMessage>(ForceAs, { it.safeCast() }) {
|
||||||
override val key: MessageKey<ForceAsLongMessage> get() = this
|
override val key: MessageKey<ForceAsLongMessage> get() = this
|
||||||
|
|
||||||
override fun toString(): String = ""
|
override fun toString(): String = ""
|
||||||
|
|
||||||
override fun <D, R> accept(visitor: MessageVisitor<D, R>, data: D): R {
|
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.OutgoingMessageSender
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
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
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
// Loaded by ServiceLoader
|
// Loaded by ServiceLoader
|
||||||
internal abstract class MessageProtocol(
|
internal abstract class MessageProtocol(
|
||||||
val priority: UInt = PRIORITY_CONTENT // the higher, the prior it being called
|
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) {
|
fun collectProcessors(processorCollector: ProcessorCollector) {
|
||||||
processorCollector.collectProcessorsImpl()
|
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.decode.*
|
||||||
import net.mamoe.mirai.internal.message.protocol.encode.*
|
import net.mamoe.mirai.internal.message.protocol.encode.*
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.*
|
import net.mamoe.mirai.internal.message.protocol.outgoing.*
|
||||||
|
import net.mamoe.mirai.internal.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.ComponentKey
|
||||||
import net.mamoe.mirai.internal.network.component.ComponentStorage
|
import net.mamoe.mirai.internal.network.component.ComponentStorage
|
||||||
import net.mamoe.mirai.internal.network.component.buildComponentStorage
|
import net.mamoe.mirai.internal.network.component.buildComponentStorage
|
||||||
import net.mamoe.mirai.internal.network.component.withFallback
|
import net.mamoe.mirai.internal.network.component.withFallback
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessResult
|
import net.mamoe.mirai.internal.pipeline.ProcessResult
|
||||||
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||||
import net.mamoe.mirai.internal.utils.structureToString
|
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.*
|
||||||
import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor
|
import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor
|
||||||
import net.mamoe.mirai.message.data.visitor.accept
|
import net.mamoe.mirai.message.data.visitor.accept
|
||||||
import net.mamoe.mirai.utils.MutableTypeSafeMap
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.TestOnly
|
|
||||||
import net.mamoe.mirai.utils.buildTypeSafeMap
|
|
||||||
import net.mamoe.mirai.utils.castUp
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@ -72,6 +76,7 @@ internal interface MessageProtocolFacade {
|
|||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
builder: MessageChainBuilder,
|
builder: MessageChainBuilder,
|
||||||
|
containingMsg: MsgComm.Msg? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +132,7 @@ internal interface MessageProtocolFacade {
|
|||||||
groupIdOrZero: Long,
|
groupIdOrZero: Long,
|
||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this) }
|
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) }
|
||||||
|
|
||||||
fun copy(): MessageProtocolFacade
|
fun copy(): MessageProtocolFacade
|
||||||
|
|
||||||
@ -235,7 +240,8 @@ internal class MessageProtocolFacadeImpl(
|
|||||||
groupIdOrZero: Long,
|
groupIdOrZero: Long,
|
||||||
messageSourceKind: MessageSourceKind,
|
messageSourceKind: MessageSourceKind,
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
builder: MessageChainBuilder
|
builder: MessageChainBuilder,
|
||||||
|
containingMsg: MsgComm.Msg?
|
||||||
) {
|
) {
|
||||||
val pipeline = decoderPipeline
|
val pipeline = decoderPipeline
|
||||||
|
|
||||||
@ -243,6 +249,7 @@ internal class MessageProtocolFacadeImpl(
|
|||||||
set(MessageDecoderContext.BOT, bot)
|
set(MessageDecoderContext.BOT, bot)
|
||||||
set(MessageDecoderContext.MESSAGE_SOURCE_KIND, messageSourceKind)
|
set(MessageDecoderContext.MESSAGE_SOURCE_KIND, messageSourceKind)
|
||||||
set(MessageDecoderContext.GROUP_ID, groupIdOrZero)
|
set(MessageDecoderContext.GROUP_ID, groupIdOrZero)
|
||||||
|
set(MessageDecoderContext.CONTAINING_MSG, containingMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
runCoroutineInPlace {
|
runCoroutineInPlace {
|
||||||
@ -297,8 +304,15 @@ internal class MessageProtocolFacadeImpl(
|
|||||||
): ProcessResult<OutgoingMessagePipelineContext, MessageReceipt<*>> {
|
): ProcessResult<OutgoingMessagePipelineContext, MessageReceipt<*>> {
|
||||||
val attributes = createAttributesForOutgoingMessage(target, message, components)
|
val attributes = createAttributesForOutgoingMessage(target, message, components)
|
||||||
|
|
||||||
val (context, _) = preprocessorPipeline.process(message.toMessageChain(), attributes)
|
val data = message.toMessageChain()
|
||||||
return outgoingPipeline.process(message.toMessageChain(), context, attributes)
|
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 {
|
override fun copy(): MessageProtocolFacade {
|
||||||
@ -327,12 +341,14 @@ internal class MessageProtocolFacadeImpl(
|
|||||||
message: Message,
|
message: Message,
|
||||||
context: ComponentStorage
|
context: ComponentStorage
|
||||||
): MutableTypeSafeMap {
|
): MutableTypeSafeMap {
|
||||||
|
val chain = message.toMessageChain()
|
||||||
val attributes = buildTypeSafeMap {
|
val attributes = buildTypeSafeMap {
|
||||||
set(OutgoingMessagePipelineContext.CONTACT, target.impl())
|
set(CONTACT, target.impl())
|
||||||
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE, message)
|
set(ORIGINAL_MESSAGE, message)
|
||||||
set(OutgoingMessagePipelineContext.ORIGINAL_MESSAGE_AS_CHAIN, message.toMessageChain())
|
set(ORIGINAL_MESSAGE_AS_CHAIN, chain)
|
||||||
set(OutgoingMessagePipelineContext.STEP, SendMessageStep.FIRST)
|
set(STEP, SendMessageStep.FIRST)
|
||||||
set(OutgoingMessagePipelineContext.COMPONENTS, thisComponentStorage.withFallback(context))
|
set(COMPONENTS, thisComponentStorage.withFallback(context))
|
||||||
|
set(MESSAGE_TO_RETRY, chain)
|
||||||
}
|
}
|
||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
package net.mamoe.mirai.internal.message.protocol.decode
|
package net.mamoe.mirai.internal.message.protocol.decode
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
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.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline
|
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||||
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
|
import net.mamoe.mirai.internal.pipeline.*
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
@ -29,6 +29,7 @@ internal interface MessageDecoderContext : ProcessorPipelineContext<ImMsgBody.El
|
|||||||
val BOT = TypeKey<Bot>("bot")
|
val BOT = TypeKey<Bot>("bot")
|
||||||
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
||||||
val GROUP_ID = TypeKey<Long>("groupId") // zero if not group
|
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 =
|
override fun createContext(data: ImMsgBody.Elem, attributes: TypeSafeMap): MessageDecoderContext =
|
||||||
MessageDecoderContextImpl(attributes)
|
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 {
|
companion object {
|
||||||
@TestOnly
|
@TestOnly
|
||||||
val defaultTraceLogging: MiraiLoggerWithSwitch by lazy {
|
val defaultTraceLogging: MiraiLoggerWithSwitch by lazy {
|
||||||
|
@ -53,7 +53,7 @@ internal class ForwardMessageProtocol : MessageProtocol() {
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
currentMessageChain = RichMessage.forwardMessage(
|
currentMessageChain += RichMessage.forwardMessage(
|
||||||
resId = resId,
|
resId = resId,
|
||||||
fileName = components[ClockHolder].local.currentTimeSeconds().toString(),
|
fileName = components[ClockHolder].local.currentTimeSeconds().toString(),
|
||||||
forwardMessage = forward,
|
forwardMessage = forward,
|
||||||
|
@ -14,11 +14,13 @@ import net.mamoe.mirai.contact.*
|
|||||||
import net.mamoe.mirai.internal.AbstractBot
|
import net.mamoe.mirai.internal.AbstractBot
|
||||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||||
import net.mamoe.mirai.internal.contact.SendMessageStep
|
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.MessageProtocol
|
||||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
|
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.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
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
|
||||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP
|
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP
|
||||||
@ -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.AtAll
|
||||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||||
import net.mamoe.mirai.message.data.content
|
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.buildTypeSafeMap
|
||||||
|
import net.mamoe.mirai.utils.info
|
||||||
import net.mamoe.mirai.utils.truncated
|
import net.mamoe.mirai.utils.truncated
|
||||||
|
|
||||||
internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_SENDER) {
|
internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_SENDER) {
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
add(GeneralMessageSender())
|
add(GeneralMessageSender(logger))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GeneralMessageSender : OutgoingMessageSender {
|
class GeneralMessageSender(
|
||||||
|
private val logger: MiraiLogger,
|
||||||
|
) : OutgoingMessageSender {
|
||||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||||
markAsConsumed()
|
markAsConsumed()
|
||||||
|
|
||||||
@ -57,28 +62,49 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
|||||||
contact = contact,
|
contact = contact,
|
||||||
message = currentMessageChain,
|
message = currentMessageChain,
|
||||||
originalMessage = attributes[ORIGINAL_MESSAGE_AS_CHAIN],
|
originalMessage = attributes[ORIGINAL_MESSAGE_AS_CHAIN],
|
||||||
fragmented = step == SendMessageStep.FRAGMENTED
|
fragmented = step == SendMessageStep.FRAGMENTED || currentMessageChain.contains(ForceAsFragmentedMessage)
|
||||||
) { source = it }
|
) { source = it }
|
||||||
|
|
||||||
sendAllPackets(bot, step, contact, packets)
|
if (sendAllPackets(bot, step, contact, packets)) {
|
||||||
|
val sourceAwait = source?.await() ?: error("Internal error: source is not initialized")
|
||||||
val sourceAwait = source?.await() ?: error("Internal error: source is not initialized")
|
sourceAwait.tryEnsureSequenceIdAvailable()
|
||||||
sourceAwait.tryEnsureSequenceIdAvailable()
|
collect(sourceAwait.createMessageReceipt(contact, true))
|
||||||
collect(sourceAwait.createMessageReceipt(contact, true))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return `true`, if source needs to be added
|
||||||
|
*/
|
||||||
private suspend fun OutgoingMessagePipelineContext.sendAllPackets(
|
private suspend fun OutgoingMessagePipelineContext.sendAllPackets(
|
||||||
bot: AbstractBot,
|
bot: AbstractBot,
|
||||||
step: SendMessageStep,
|
step: SendMessageStep,
|
||||||
contact: Contact,
|
contact: Contact,
|
||||||
packets: List<OutgoingPacket>
|
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 originalMessage = attributes[ORIGINAL_MESSAGE]
|
||||||
val protocolStrategy = components[MessageProtocolStrategy]
|
val protocolStrategy = components[MessageProtocolStrategy]
|
||||||
val finalMessage = currentMessageChain
|
val finalMessage = currentMessageChain
|
||||||
|
|
||||||
val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response
|
val resp = protocolStrategy.sendPacket(bot, packet) as MessageSvcPbSendMsg.Response
|
||||||
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
|
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
|
||||||
|
logger.info { "STEP $step: message too large." }
|
||||||
val next = step.nextStepOrNull()
|
val next = step.nextStepOrNull()
|
||||||
?: throw MessageTooLargeException(
|
?: throw MessageTooLargeException(
|
||||||
contact,
|
contact,
|
||||||
@ -88,13 +114,16 @@ internal class GeneralMessageSenderProtocol : MessageProtocol(PRIORITY_GENERAL_S
|
|||||||
)
|
)
|
||||||
|
|
||||||
// retry with next step
|
// retry with next step
|
||||||
processAlso(
|
logger.info { "Retrying with STEP $next" }
|
||||||
originalMessage.toMessageChain(),
|
val (_, receipts) = processAlso(
|
||||||
|
attributes[MESSAGE_TO_RETRY],
|
||||||
extraAttributes = buildTypeSafeMap {
|
extraAttributes = buildTypeSafeMap {
|
||||||
set(STEP, next)
|
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) {
|
if (resp is MessageSvcPbSendMsg.Response.ServiceUnavailable) {
|
||||||
throw IllegalStateException("Send message to $contact failed, server service is unavailable.")
|
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) {
|
check(resp is MessageSvcPbSendMsg.Response.SUCCESS) {
|
||||||
"Send message failed: $resp"
|
"Send message failed: $resp"
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.Group
|
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.MessageProtocol
|
||||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||||
import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoder
|
import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoder
|
||||||
@ -45,24 +47,35 @@ internal class MusicShareProtocol : MessageProtocol() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Sender : OutgoingMessageSender {
|
open class Sender : OutgoingMessageSender {
|
||||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||||
val contact = attributes[CONTACT]
|
|
||||||
val bot = contact.bot
|
|
||||||
val musicShare = currentMessageChain[MusicShare] ?: return
|
val musicShare = currentMessageChain[MusicShare] ?: return
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val packet = MusicSharePacket(
|
val contact = attributes[CONTACT]
|
||||||
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 strategy = components[MessageProtocolStrategy]
|
val strategy = components[MessageProtocolStrategy]
|
||||||
|
val bot = contact.bot
|
||||||
|
|
||||||
|
sendMusicSharePacket(bot, musicShare, contact, strategy)
|
||||||
|
|
||||||
val source = strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 3116)
|
val source = strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 3116)
|
||||||
source.tryEnsureSequenceIdAvailable()
|
source.tryEnsureSequenceIdAvailable()
|
||||||
|
|
||||||
collect(source.createMessageReceipt(contact, true))
|
collect(source.createMessageReceipt(contact, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.group.GroupMessageProcessor
|
||||||
import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageProcessor
|
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.OutgoingPacket
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||||
@ -34,6 +35,13 @@ internal interface MessageProtocolStrategy<in C : AbstractContact> {
|
|||||||
return bot.network.sendAndExpect(packet)
|
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(
|
suspend fun createPacketsForGeneralMessage(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
contact: C,
|
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.message.source.ensureSequenceIdAvailable
|
||||||
import net.mamoe.mirai.internal.network.component.ComponentStorage
|
import net.mamoe.mirai.internal.network.component.ComponentStorage
|
||||||
import net.mamoe.mirai.internal.network.handler.logger
|
import net.mamoe.mirai.internal.network.handler.logger
|
||||||
import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline
|
import net.mamoe.mirai.internal.pipeline.*
|
||||||
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.utils.estimateLength
|
import net.mamoe.mirai.internal.utils.estimateLength
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
@ -38,12 +35,21 @@ internal interface OutgoingMessagePipeline :
|
|||||||
|
|
||||||
internal open class OutgoingMessagePipelineImpl :
|
internal open class OutgoingMessagePipelineImpl :
|
||||||
AbstractProcessorPipeline<OutgoingMessagePipelineProcessor, OutgoingMessagePipelineContext, OutgoingMessagePipelineInput, MessageReceipt<*>>(
|
AbstractProcessorPipeline<OutgoingMessagePipelineProcessor, OutgoingMessagePipelineContext, OutgoingMessagePipelineInput, MessageReceipt<*>>(
|
||||||
PipelineConfiguration(stopWhenConsumed = false), @OptIn(TestOnly::class) defaultTraceLogging
|
PipelineConfiguration(stopWhenConsumed = true), @OptIn(TestOnly::class) defaultTraceLogging
|
||||||
), OutgoingMessagePipeline {
|
), OutgoingMessagePipeline {
|
||||||
|
|
||||||
inner class OutgoingMessagePipelineContextImpl(
|
inner class OutgoingMessagePipelineContextImpl(
|
||||||
attributes: TypeSafeMap, override var currentMessageChain: MessageChain
|
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(
|
override fun createContext(
|
||||||
data: OutgoingMessagePipelineInput, attributes: TypeSafeMap
|
data: OutgoingMessagePipelineInput, attributes: TypeSafeMap
|
||||||
@ -61,6 +67,9 @@ internal open class OutgoingMessagePipelineImpl :
|
|||||||
|
|
||||||
internal interface OutgoingMessagePipelineContext :
|
internal interface OutgoingMessagePipelineContext :
|
||||||
ProcessorPipelineContext<OutgoingMessagePipelineInput, MessageReceipt<*>> {
|
ProcessorPipelineContext<OutgoingMessagePipelineInput, MessageReceipt<*>> {
|
||||||
|
/**
|
||||||
|
* Current message chain updated throughout the process. Will be updated from the [sub-processes][processAlso].
|
||||||
|
*/
|
||||||
var currentMessageChain: MessageChain
|
var currentMessageChain: MessageChain
|
||||||
|
|
||||||
suspend fun MessageSource.tryEnsureSequenceIdAvailable() {
|
suspend fun MessageSource.tryEnsureSequenceIdAvailable() {
|
||||||
@ -110,6 +119,10 @@ internal interface OutgoingMessagePipelineContext :
|
|||||||
*/
|
*/
|
||||||
val ORIGINAL_MESSAGE_AS_CHAIN = TypeKey<MessageChain>("originalMessageAsChain")
|
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
|
* Message target
|
||||||
|
@ -49,6 +49,10 @@ internal interface MessageVisitorEx<in D, out R> : MessageVisitor<D, R> {
|
|||||||
return visitInternalFlagOnlyMessage(message, data)
|
return visitInternalFlagOnlyMessage(message, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visitForceAsFragmentedMessage(message: ForceAsFragmentedMessage, data: D): R {
|
||||||
|
return visitInternalFlagOnlyMessage(message, data)
|
||||||
|
}
|
||||||
|
|
||||||
fun visitDontAsLongMessage(message: DontAsLongMessage, data: D): R {
|
fun visitDontAsLongMessage(message: DontAsLongMessage, data: D): R {
|
||||||
return visitInternalFlagOnlyMessage(message, data)
|
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.network.toPacket
|
||||||
import net.mamoe.mirai.internal.pipeline.*
|
import net.mamoe.mirai.internal.pipeline.*
|
||||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||||
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@ -90,17 +89,6 @@ internal open class NoticeProcessorPipelineImpl protected constructor(
|
|||||||
) : BaseContextImpl(attributes), NoticePipelineContext {
|
) : BaseContextImpl(attributes), NoticePipelineContext {
|
||||||
override val bot: QQAndroidBot
|
override val bot: QQAndroidBot
|
||||||
get() = this@NoticeProcessorPipelineImpl.bot
|
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(
|
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.NestedStructure
|
||||||
import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
|
import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
|
||||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
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.internal.utils.structureToStringIfAvailable
|
||||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||||
import net.mamoe.mirai.utils.unzip
|
import net.mamoe.mirai.utils.unzip
|
||||||
@ -1005,11 +1006,18 @@ internal class ImMsgBody : ProtoBuf {
|
|||||||
@ProtoNumber(6) @JvmField val type: Int = 0,
|
@ProtoNumber(6) @JvmField val type: Int = 0,
|
||||||
@ProtoNumber(7) @JvmField val richMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoNumber(7) @JvmField val richMsg: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
@ProtoNumber(8) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoNumber(8) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
|
@NestedStructure(SrcMsgDesensitizer::class)
|
||||||
@ProtoNumber(9) @JvmField val srcMsg: ByteArray? = null,
|
@ProtoNumber(9) @JvmField val srcMsg: ByteArray? = null,
|
||||||
@ProtoNumber(10) @JvmField val toUin: Long = 0L,
|
@ProtoNumber(10) @JvmField val toUin: Long = 0L,
|
||||||
@ProtoNumber(11) @JvmField val troopName: ByteArray = EMPTY_BYTE_ARRAY,
|
@ProtoNumber(11) @JvmField val troopName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
|
internal object SrcMsgDesensitizer : NestedStructureDesensitizer<SourceMsg, MsgComm.Msg> {
|
||||||
|
override fun deserialize(context: SourceMsg, byteArray: ByteArray): MsgComm.Msg {
|
||||||
|
return byteArray.loadAs(MsgComm.Msg.serializer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class Text(
|
internal class Text(
|
||||||
@ProtoNumber(1) @JvmField val str: String = "",
|
@ProtoNumber(1) @JvmField val str: String = "",
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
package net.mamoe.mirai.internal.pipeline
|
package net.mamoe.mirai.internal.pipeline
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
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.network.components.NoticeProcessor
|
||||||
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
@ -39,6 +40,7 @@ internal interface ProcessorPipeline<P : Processor<C, D>, C : ProcessorPipelineC
|
|||||||
|
|
||||||
fun registerBefore(processor: P): DisposableRegistry
|
fun registerBefore(processor: P): DisposableRegistry
|
||||||
|
|
||||||
|
fun createContext(data: D, attributes: TypeSafeMap): C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process using the [context].
|
* Process using the [context].
|
||||||
@ -139,7 +141,7 @@ internal interface ProcessorPipelineContext<D, R> {
|
|||||||
annotation class ConsumptionMarker // to give an explicit color.
|
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
|
* @param extraAttributes extra attributes
|
||||||
* @return result collected from processors. This would also have been collected to this context (where you call [processAlso]).
|
* @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(
|
abstract inner class BaseContextImpl(
|
||||||
attributes: TypeSafeMap,
|
attributes: TypeSafeMap,
|
||||||
) : AbstractProcessorPipelineContext<D, R>(attributes, traceLogging) {
|
) : AbstractProcessorPipelineContext<D, R>(attributes, traceLogging) {
|
||||||
@ -226,7 +226,10 @@ protected constructor(
|
|||||||
extraAttributes: TypeSafeMap
|
extraAttributes: TypeSafeMap
|
||||||
): ProcessResult<out ProcessorPipelineContext<D, R>, R> {
|
): ProcessResult<out ProcessorPipelineContext<D, R>, R> {
|
||||||
traceLogging.info { "processAlso: data=${data.structureToStringAndDesensitizeIfAvailable()}" }
|
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
|
this.collected.data += it.collected
|
||||||
traceLogging.info { "processAlso: result=$it" }
|
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.ContactOrBot
|
||||||
import net.mamoe.mirai.contact.Friend
|
import net.mamoe.mirai.contact.Friend
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.contact.MemberPermission
|
||||||
import net.mamoe.mirai.internal.AbstractBot
|
import net.mamoe.mirai.internal.AbstractBot
|
||||||
|
import net.mamoe.mirai.internal.BotAccount
|
||||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
import net.mamoe.mirai.internal.contact.AbstractContact
|
||||||
import net.mamoe.mirai.internal.message.data.inferMessageSourceKind
|
import net.mamoe.mirai.internal.message.data.inferMessageSourceKind
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||||
@ -41,6 +43,7 @@ import net.mamoe.mirai.internal.notice.processors.GroupExtensions
|
|||||||
import net.mamoe.mirai.internal.test.runBlockingUnit
|
import net.mamoe.mirai.internal.test.runBlockingUnit
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.utils.Clock
|
import net.mamoe.mirai.utils.Clock
|
||||||
|
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||||
import net.mamoe.mirai.utils.md5
|
import net.mamoe.mirai.utils.md5
|
||||||
import net.mamoe.mirai.utils.toUHexString
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
@ -52,9 +55,19 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.asserter
|
import kotlin.test.asserter
|
||||||
|
|
||||||
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions {
|
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 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 decoderLoggerEnabled = false
|
||||||
private var encoderLoggerEnabled = false
|
private var encoderLoggerEnabled = false
|
||||||
@ -144,8 +157,8 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
var messages: MessageChainBuilder = MessageChainBuilder()
|
var messages: MessageChainBuilder = MessageChainBuilder()
|
||||||
|
|
||||||
var groupIdOrZero: Long = 0
|
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 withGeneralFlags = true
|
||||||
var isForward = false
|
var isForward = false
|
||||||
|
|
||||||
@ -157,12 +170,10 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
messages.addAll(message)
|
messages.addAll(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun target(target: ContactOrBot?) {
|
fun target(target: ContactOrBot) {
|
||||||
this.target = target
|
this.target = target
|
||||||
|
|
||||||
if (target != null) {
|
messageSourceKind = target.inferMessageSourceKind()
|
||||||
messageSourceKind = target.inferMessageSourceKind()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target is Group) {
|
if (target is Group) {
|
||||||
groupIdOrZero = target.id
|
groupIdOrZero = target.id
|
||||||
@ -232,51 +243,53 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
// sending
|
// sending
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
init {
|
open inner class TestMessageProtocolStrategy : MessageProtocolStrategy<AbstractContact> {
|
||||||
components[MessageProtocolStrategy] = object : MessageProtocolStrategy<AbstractContact> {
|
override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet {
|
||||||
override suspend fun sendPacket(bot: AbstractBot, packet: OutgoingPacket): Packet {
|
assertEquals(0x123, packet.sequenceId)
|
||||||
assertEquals(0x123, packet.sequenceId)
|
return MessageSvcPbSendMsg.Response.SUCCESS
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
components[HighwayUploader] = object : HighwayUploader {
|
||||||
override suspend fun uploadMessages(
|
override suspend fun uploadMessages(
|
||||||
contact: AbstractContact,
|
contact: AbstractContact,
|
||||||
@ -300,7 +313,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
fun runWithFacade(action: suspend MessageProtocolFacade.() -> Unit) {
|
fun runWithFacade(action: suspend MessageProtocolFacade.() -> Unit) {
|
||||||
runBlockingUnit {
|
runBlockingUnit {
|
||||||
facadeOf(*protocols).run { action() }
|
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
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
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.LightMessageRefiner.dropMiraiInternalFlags
|
||||||
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
||||||
import net.mamoe.mirai.internal.message.flags.ForceAsLongMessage
|
import net.mamoe.mirai.internal.message.flags.ForceAsLongMessage
|
||||||
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
|
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.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.Image
|
||||||
import net.mamoe.mirai.message.data.repeat
|
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.message.data.toPlainText
|
||||||
import net.mamoe.mirai.utils.castUp
|
import net.mamoe.mirai.utils.castUp
|
||||||
import net.mamoe.mirai.utils.getRandomString
|
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
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
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.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.LightApp
|
||||||
import net.mamoe.mirai.message.data.MessageOrigin
|
import net.mamoe.mirai.message.data.MessageOrigin
|
||||||
import net.mamoe.mirai.message.data.MessageOriginKind
|
import net.mamoe.mirai.message.data.MessageOriginKind
|
||||||
import net.mamoe.mirai.message.data.MusicKind.NeteaseCloudMusic
|
import net.mamoe.mirai.message.data.MusicKind.NeteaseCloudMusic
|
||||||
import net.mamoe.mirai.message.data.MusicShare
|
import net.mamoe.mirai.message.data.MusicShare
|
||||||
|
import net.mamoe.mirai.utils.castUp
|
||||||
import net.mamoe.mirai.utils.hexToBytes
|
import net.mamoe.mirai.utils.hexToBytes
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
||||||
override val protocols: Array<out MessageProtocol> =
|
override val protocols: Array<out MessageProtocol> =
|
||||||
arrayOf(TextProtocol(), MusicShareProtocol(), RichMessageProtocol())
|
arrayOf(TextProtocol(), MusicShareProtocol(), RichMessageProtocol(), GeneralMessageSenderProtocol())
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun `init group`() {
|
fun `init group`() {
|
||||||
@ -73,5 +80,40 @@ internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
}.doDecoderChecks()
|
}.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
|
* 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
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||||
import net.mamoe.mirai.internal.message.protocol.decodeAndRefineLight
|
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
||||||
import net.mamoe.mirai.message.data.Face
|
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
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.message.data.messageChainOf
|
||||||
import net.mamoe.mirai.utils.hexToBytes
|
import net.mamoe.mirai.utils.hexToBytes
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@ -57,8 +25,9 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
override val protocols: Array<out MessageProtocol> = arrayOf(QuoteReplyProtocol(), TextProtocol())
|
override val protocols: Array<out MessageProtocol> = arrayOf(QuoteReplyProtocol(), TextProtocol())
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `decode group reference group`() {
|
fun `decode referencing online group message in group`() {
|
||||||
buildCodingChecks {
|
buildCodingChecks {
|
||||||
|
targetGroup()
|
||||||
elem(
|
elem(
|
||||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||||
@ -82,38 +51,388 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// message(
|
message(
|
||||||
// QuoteReply(
|
QuoteReply(
|
||||||
//// OfflineMessageSourceImplData(
|
OfflineMessageSourceImplData(
|
||||||
////
|
ids = intArrayOf(1803),
|
||||||
//// )
|
internalIds = intArrayOf(539443883),
|
||||||
// )
|
time = 1653147259,
|
||||||
// )
|
originalMessage = messageChainOf(PlainText("a")),
|
||||||
targetGroup()
|
kind = messageSourceKind,
|
||||||
useOrdinaryEquality()
|
fromId = 1230001,
|
||||||
|
targetId = 1,
|
||||||
|
botId = bot.id,
|
||||||
|
)
|
||||||
|
), PlainText("s")
|
||||||
|
)
|
||||||
}.doDecoderChecks()
|
}.doDecoderChecks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can decode`() {
|
fun `encode referencing offline group message in group`() {
|
||||||
doDecoderChecks(
|
buildCodingChecks {
|
||||||
messageChainOf(Face(Face.YIN_XIAN)),
|
targetGroup()
|
||||||
) {
|
elem(
|
||||||
decodeAndRefineLight(
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
listOf(
|
srcMsg = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.SourceMsg(
|
||||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
origSeqs = intArrayOf(-31257),
|
||||||
face = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Face(
|
senderUin = 1230001,
|
||||||
index = 108,
|
time = 1653326514,
|
||||||
old = "14 AD".hexToBytes(),
|
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() {
|
private fun CodingChecksBuilder.targetGroup() {
|
||||||
@ -121,7 +440,7 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun CodingChecksBuilder.targetFriend() {
|
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 kotlinx.coroutines.SupervisorJob
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.internal.BotAccount
|
||||||
import net.mamoe.mirai.internal.MockBot
|
import net.mamoe.mirai.internal.MockBot
|
||||||
import net.mamoe.mirai.internal.QQAndroidBot
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
|
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
|
||||||
@ -31,6 +32,8 @@ import net.mamoe.mirai.internal.utils.subLogger
|
|||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import network.framework.components.TestEventDispatcherImpl
|
import network.framework.components.TestEventDispatcherImpl
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.random.Random
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,9 +44,13 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT
|
|||||||
protected open fun createNetworkHandlerContext() = TestNetworkHandlerContext(bot, logger, components)
|
protected open fun createNetworkHandlerContext() = TestNetworkHandlerContext(bot, logger, components)
|
||||||
protected open fun createNetworkHandler() = TestNetworkHandler(bot, createNetworkHandlerContext())
|
protected open fun createNetworkHandler() = TestNetworkHandler(bot, createNetworkHandlerContext())
|
||||||
|
|
||||||
protected val bot: QQAndroidBot = MockBot {
|
protected open fun createAccount() = BotAccount(Random.nextLong().absoluteValue.mod(1000L), "pwd")
|
||||||
nhProvider = { createNetworkHandler() }
|
|
||||||
additionalComponentsProvider = { this@AbstractMockNetworkHandlerTest.components }
|
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 logger = MiraiLogger.Factory.create(Bot::class, "test")
|
||||||
protected val components = ConcurrentComponentStorage().apply {
|
protected val components = ConcurrentComponentStorage().apply {
|
||||||
|
@ -37,11 +37,8 @@ import kotlin.test.assertTrue
|
|||||||
internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
||||||
private val bot = MockBot()
|
private val bot = MockBot()
|
||||||
|
|
||||||
/**
|
|
||||||
* This test is very ugly, but we cannot do anything else.
|
|
||||||
*/ // We need #1304
|
|
||||||
@Test
|
@Test
|
||||||
fun `refine ForwardMessageInternal for MessageReceipt`() = runBlockingUnit {
|
fun `refine ForwardMessageInternal for MessageReceipt's original MessageChain`() = runBlockingUnit {
|
||||||
val group = bot.addGroup(123, 2)
|
val group = bot.addGroup(123, 2)
|
||||||
|
|
||||||
val forward = buildForwardMessage(group) {
|
val forward = buildForwardMessage(group) {
|
||||||
@ -52,7 +49,7 @@ internal class MessageReceiptTest : AbstractTest(), GroupExtensions {
|
|||||||
val facade = MessageProtocolFacade.INSTANCE.copy()
|
val facade = MessageProtocolFacade.INSTANCE.copy()
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
facade.preprocessorPipeline.replaceProcessor(
|
facade.outgoingPipeline.replaceProcessor(
|
||||||
{ it is GeneralMessageSenderProtocol.GeneralMessageSender },
|
{ it is GeneralMessageSenderProtocol.GeneralMessageSender },
|
||||||
OutgoingMessageProcessorAdapter(object : OutgoingMessageSender {
|
OutgoingMessageProcessorAdapter(object : OutgoingMessageSender {
|
||||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
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.getMiraiImpl
|
||||||
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
|
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
|
||||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
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.RefinableMessage
|
||||||
import net.mamoe.mirai.internal.message.RefineContext
|
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.message.source.OfflineMessageSourceImplData
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
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) {
|
private suspend fun testRecursiveRefine(list: List<ImMsgBody.Elem>, expected: MessageChain, isLight: Boolean) {
|
||||||
val actual = buildMessageChain {
|
val actual = buildMessageChain {
|
||||||
ReceiveMessageTransformer.joinToMessageChain(list, 0, MessageSourceKind.GROUP, bot, this)
|
MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, bot, this, null)
|
||||||
}.let { c ->
|
}.let { c ->
|
||||||
if (isLight) {
|
if (isLight) {
|
||||||
c.refineLight(bot)
|
c.refineLight(bot)
|
||||||
|
Loading…
Reference in New Issue
Block a user