commonMain.message.protocol.outgoing.OutgoingMessagePipeline.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mirai-core-jvm Show documentation
Show all versions of mirai-core-jvm Show documentation
Mirai Protocol implementation for QQ Android
/*
* 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.outgoing
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.MessageTooLargeException
import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.SendMessageStep
import net.mamoe.mirai.internal.message.source.ensureSequenceIdAvailable
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.pipeline.*
import net.mamoe.mirai.internal.utils.estimateLength
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.*
///////////////////////////////////////////////////////////////////////////
// Infrastructure for a ProcessorPipeline
///////////////////////////////////////////////////////////////////////////
// Just to change this easily — you'd know it's actually Unit — a placeholder.
internal typealias OutgoingMessagePipelineInput = MessageChain
internal interface OutgoingMessagePipeline :
ProcessorPipeline>
internal open class OutgoingMessagePipelineImpl :
AbstractProcessorPipeline>(
PipelineConfiguration(stopWhenConsumed = true), @OptIn(TestOnly::class) defaultTraceLogging
), OutgoingMessagePipeline {
inner class OutgoingMessagePipelineContextImpl(
attributes: TypeSafeMap, override var currentMessageChain: MessageChain
) : OutgoingMessagePipelineContext, BaseContextImpl(attributes) {
override suspend fun processAlso(
data: OutgoingMessagePipelineInput,
extraAttributes: TypeSafeMap
): ProcessResult>, MessageReceipt<*>> {
return super.processAlso(data, extraAttributes).also { (context, _) ->
this.currentMessageChain = (context as OutgoingMessagePipelineContext).currentMessageChain
}
}
}
override fun createContext(
data: OutgoingMessagePipelineInput, attributes: TypeSafeMap
): OutgoingMessagePipelineContext = OutgoingMessagePipelineContextImpl(attributes, data)
companion object {
@TestOnly
val defaultTraceLogging: MiraiLoggerWithSwitch by lazy {
MiraiLogger.Factory.create(OutgoingMessagePipelineImpl::class, "OutgoingMessagePipeline")
.withSwitch(systemProp("mirai.message.outgoing.pipeline.log.full", false))
}
}
}
internal interface OutgoingMessagePipelineContext :
ProcessorPipelineContext> {
/**
* Current message chain updated throughout the process. Will be updated from the [sub-processes][processAlso].
*/
var currentMessageChain: MessageChain
suspend fun MessageSource.tryEnsureSequenceIdAvailable() {
val contact = attributes[CONTACT]
val bot = contact.bot
try {
ensureSequenceIdAvailable()
} catch (e: Exception) {
bot.network.logger.warning(
"Timeout awaiting sequenceId for message(${currentMessageChain.content.take(10)}). Some features may not work properly.",
e
)
}
}
fun Iterable.countImages(): Int = this.count { it is Image }
fun Iterable.verifyLength(
originalMessage: Message, target: Contact,
): Int {
val chain = this
val length = estimateLength(target, 15001)
if (length > 15000 || countImages() > 50) {
throw MessageTooLargeException(
target, originalMessage, this.toMessageChain(),
"message(${
chain.joinToString("", limit = 10).let { rsp ->
if (rsp.length > 100) {
rsp.take(100) + "..."
} else rsp
}
}) is too large. Allow up to 50 images or 5000 chars"
)
}
return length
}
companion object {
/**
* Original
*/
val ORIGINAL_MESSAGE = TypeKey("originalMessage")
/**
* You should only use [ORIGINAL_MESSAGE_AS_CHAIN] if you can't use [ORIGINAL_MESSAGE]
*/
val ORIGINAL_MESSAGE_AS_CHAIN = TypeKey("originalMessageAsChain")
/**
* Message chain used when retrying with next [step][SendMessageStep]s.
*/
val MESSAGE_TO_RETRY = TypeKey("messageToRetry")
/**
* Message target
*/
val CONTACT = TypeKey("contact")
val STEP = TypeKey("step")
val COMPONENTS = TypeKey("components")
val OutgoingMessagePipelineContext.components: ComponentStorage get() = attributes[COMPONENTS]
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy