commonMain.message.protocol.MessageProtocolFacade.kt Maven / Gradle / Ivy
/*
* 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
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.SendMessageStep
import net.mamoe.mirai.internal.contact.impl
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
import net.mamoe.mirai.internal.message.EmptyRefineContext
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
import net.mamoe.mirai.internal.message.RefineContext
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.message.protocol.decode.*
import net.mamoe.mirai.internal.message.protocol.encode.*
import net.mamoe.mirai.internal.message.protocol.outgoing.*
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.COMPONENTS
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.CONTACT
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.MESSAGE_TO_RETRY
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.ORIGINAL_MESSAGE_AS_CHAIN
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext.Companion.STEP
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.component.buildComponentStorage
import net.mamoe.mirai.internal.network.component.withFallback
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.pipeline.ProcessResult
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
import net.mamoe.mirai.internal.utils.structureToString
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor
import net.mamoe.mirai.message.data.visitor.accept
import net.mamoe.mirai.utils.*
import java.util.*
import kotlin.reflect.KClass
internal interface MessageProtocolFacade {
val remark: String get() = "MessageProtocolFacade"
val encoderPipeline: MessageEncoderPipeline
val decoderPipeline: MessageDecoderPipeline
val preprocessorPipeline: OutgoingMessagePipeline
val outgoingPipeline: OutgoingMessagePipeline
val loaded: List
/**
* Encode high-level [MessageChain] to give list of low-level and protocol-specific [ImMsgBody.Elem]s.
*/
fun encode(
chain: MessageChain,
messageTarget: ContactOrBot?, // for At.display, QuoteReply, Image, and more.
withGeneralFlags: Boolean, // important for RichMessages, may also be helpful for others
isForward: Boolean = false, // is inside forward, for At.display
): List
/**
* Decode list of low-level and protocol-specific [ImMsgBody.Elem]s to give a high-level [MessageChain].
*
* [SingleMessage]s are appended to the [builder].
*/
fun decode(
elements: List,
groupIdOrZero: Long,
messageSourceKind: MessageSourceKind,
bot: Bot,
builder: MessageChainBuilder,
containingMsg: MsgComm.Msg? = null,
)
/**
* Pre-process a message
* @see OutgoingMessagePreprocessor
*/
suspend fun preprocess(
target: C,
message: Message,
components: ComponentStorage,
): MessageChain
/**
* Send a message
* @see OutgoingMessageProcessor
*/
suspend fun sendOutgoing(
target: C,
message: Message,
components: ComponentStorage,
): MessageReceipt
/**
* Preprocess and send a message
* @see OutgoingMessagePreprocessor
* @see OutgoingMessageProcessor
*/
suspend fun preprocessAndSendOutgoing(
target: C,
message: Message,
components: ComponentStorage,
): MessageReceipt
/**
* Preprocess and send a message
* @see OutgoingMessagePreprocessor
* @see OutgoingMessageProcessor
*/
@TestOnly
suspend fun preprocessAndSendOutgoingImpl(
target: C,
message: Message,
components: ComponentStorage,
): ProcessResult>
/**
* Decode list of low-level and protocol-specific [ImMsgBody.Elem]s to give a high-level [MessageChain].
*/
fun decode(
elements: List,
groupIdOrZero: Long,
messageSourceKind: MessageSourceKind,
bot: Bot,
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) }
fun copy(): MessageProtocolFacade
/**
* The default global instance.
*/
companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl(),
ComponentKey
}
internal fun MessageProtocolFacade.decodeAndRefineLight(
elements: List,
groupIdOrZero: Long,
messageSourceKind: MessageSourceKind,
bot: Bot,
refineContext: RefineContext = EmptyRefineContext
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineLight(bot, refineContext)
internal suspend fun MessageProtocolFacade.decodeAndRefineDeep(
elements: List,
groupIdOrZero: Long,
messageSourceKind: MessageSourceKind,
bot: Bot,
refineContext: RefineContext = EmptyRefineContext
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineDeep(bot, refineContext)
internal class MessageProtocolFacadeImpl(
private val protocols: Iterable = ServiceLoader.load(MessageProtocol::class.java),
override val remark: String = "MessageProtocolFacade"
) : MessageProtocolFacade {
override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl()
override val decoderPipeline: MessageDecoderPipeline = MessageDecoderPipelineImpl()
override val preprocessorPipeline: OutgoingMessagePipeline = OutgoingMessagePipelineImpl()
override val outgoingPipeline: OutgoingMessagePipeline = OutgoingMessagePipelineImpl()
override val loaded: List = kotlin.run {
val instances: PriorityQueue = protocols
.toCollection(PriorityQueue(MessageProtocol.PriorityComparator.reversed()))
for (instance in instances) {
instance.collectProcessors(object : ProcessorCollector() {
override fun add(encoder: MessageEncoder, elementType: KClass) {
[email protected](
MessageEncoderProcessor(
encoder,
elementType
)
)
}
override fun add(decoder: MessageDecoder) {
[email protected](MessageDecoderProcessor(decoder))
}
override fun add(preprocessor: OutgoingMessagePreprocessor) {
preprocessorPipeline.registerProcessor(OutgoingMessageProcessorAdapter(preprocessor))
}
override fun add(transformer: OutgoingMessageTransformer) {
outgoingPipeline.registerProcessor(OutgoingMessageProcessorAdapter(transformer))
}
override fun add(sender: OutgoingMessageSender) {
outgoingPipeline.registerProcessor(OutgoingMessageProcessorAdapter(sender))
}
override fun add(postprocessor: OutgoingMessagePostprocessor) {
outgoingPipeline.registerProcessor(OutgoingMessageProcessorAdapter(postprocessor))
}
})
}
instances.toList()
}
override fun encode(
chain: MessageChain,
messageTarget: ContactOrBot?,
withGeneralFlags: Boolean,
isForward: Boolean
): List {
val pipeline = encoderPipeline
val attributes = buildTypeSafeMap {
set(MessageEncoderContext.CONTACT, messageTarget)
set(MessageEncoderContext.ORIGINAL_MESSAGE, chain)
set(MessageEncoderContext.ADD_GENERAL_FLAGS, withGeneralFlags)
set(MessageEncoderContext.IS_FORWARD, isForward)
}
val builder = ArrayList(chain.size)
chain.accept(object : RecursiveMessageVisitor() {
override fun visitSingleMessage(message: SingleMessage, data: Unit) {
runCoroutineInPlace {
builder.addAll(pipeline.process(message, attributes).collected)
}
}
})
return builder
}
override fun decode(
elements: List,
groupIdOrZero: Long,
messageSourceKind: MessageSourceKind,
bot: Bot,
builder: MessageChainBuilder,
containingMsg: MsgComm.Msg?
) {
val pipeline = decoderPipeline
val attributes = buildTypeSafeMap {
set(MessageDecoderContext.BOT, bot)
set(MessageDecoderContext.MESSAGE_SOURCE_KIND, messageSourceKind)
set(MessageDecoderContext.GROUP_ID, groupIdOrZero)
set(MessageDecoderContext.CONTAINING_MSG, containingMsg)
}
runCoroutineInPlace {
elements.forEach { builder.addAll(pipeline.process(it, attributes).collected) }
}
}
private val thisComponentStorage by lazy {
buildComponentStorage {
set(
MessageProtocolFacade,
this@MessageProtocolFacadeImpl
)
}
}
override suspend fun preprocess(
target: C,
message: Message,
components: ComponentStorage
): MessageChain {
val attributes = createAttributesForOutgoingMessage(target, message, components)
return preprocessorPipeline.process(message.toMessageChain(), attributes).context.currentMessageChain
}
override suspend fun sendOutgoing(
target: C, message: Message,
components: ComponentStorage
): MessageReceipt {
val attributes = createAttributesForOutgoingMessage(target, message, components)
val (_, result) = outgoingPipeline.process(message.toMessageChain(), attributes)
return getSingleReceipt(result, message)
}
override suspend fun preprocessAndSendOutgoing(
target: C,
message: Message,
components: ComponentStorage
): MessageReceipt {
@OptIn(TestOnly::class)
return getSingleReceipt(preprocessAndSendOutgoingImpl(target, message, components).collected, message)
}
@TestOnly
override suspend fun preprocessAndSendOutgoingImpl(
target: C,
message: Message,
components: ComponentStorage
): ProcessResult> {
val attributes = createAttributesForOutgoingMessage(target, message, components)
val data = message.toMessageChain()
val (context, _) = preprocessorPipeline.process(data, attributes)
val preprocessed = context.currentMessageChain
return outgoingPipeline.process(
data,
outgoingPipeline.createContext(preprocessed, context.attributes.plus(MESSAGE_TO_RETRY to preprocessed)),
attributes
)
}
override fun copy(): MessageProtocolFacade {
return MessageProtocolFacadeImpl(protocols)
}
private fun getSingleReceipt(
result: Collection>,
message: Message
): MessageReceipt {
when (result.size) {
0 -> throw contextualBugReportException(
"Internal error: no MessageReceipt was returned from OutgoingMessagePipeline for message",
forDebug = message.structureToString()
)
1 -> return result.single().castUp()
else -> throw contextualBugReportException(
"Internal error: multiple MessageReceipts were returned from OutgoingMessagePipeline: $result",
forDebug = message.structureToString()
)
}
}
private fun createAttributesForOutgoingMessage(
target: C,
message: Message,
context: ComponentStorage
): MutableTypeSafeMap {
val chain = message.toMessageChain()
val attributes = buildTypeSafeMap {
set(CONTACT, target.impl())
set(ORIGINAL_MESSAGE, message)
set(ORIGINAL_MESSAGE_AS_CHAIN, chain)
set(STEP, SendMessageStep.FIRST)
set(COMPONENTS, thisComponentStorage.withFallback(context))
set(MESSAGE_TO_RETRY, chain)
}
return attributes
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy