mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-05 04:52:23 +08:00
Integrate new MessageProtocol with existing code
This commit is contained in:
parent
c47779c726
commit
a89f6aeaef
23
.run/RunMessageDecodingRecorderKt.run.xml
Normal file
23
.run/RunMessageDecodingRecorderKt.run.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="RunMessageDecodingRecorderKt" type="JetRunConfigurationType"
|
||||||
|
nameIsGenerated="true">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="net.mamoe.mirai.internal.bootstrap.RunMessageDecodingRecorderKt"/>
|
||||||
|
<module name="mirai.mirai-core.jvmTest"/>
|
||||||
|
<shortenClasspath name="NONE"/>
|
||||||
|
<option name="VM_PARAMETERS"
|
||||||
|
value="-Dmirai.debug.network.state.observer.logging=true -Dmirai.debug.network.show.all.components=true -Dkotlinx.coroutines.debug=on -Dmirai.debug.network.show.packet.details=true"/>
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test/run"/>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true"/>
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 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.
|
* 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/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:JvmMultifileClass
|
@file:JvmMultifileClass
|
||||||
|
@ -17,10 +17,18 @@ import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.joinToMessageC
|
|||||||
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
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoderContext.Companion.BOT
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoderContext.Companion.GROUP_ID
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoderContext.Companion.MESSAGE_SOURCE_KIND
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol.Companion.UNSUPPORTED_POKE_MESSAGE_PLAIN
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol.Companion.UNSUPPORTED_MERGED_MESSAGE_PLAIN
|
||||||
import net.mamoe.mirai.internal.message.source.*
|
import net.mamoe.mirai.internal.message.source.*
|
||||||
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
|
||||||
|
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
|
import net.mamoe.mirai.utils.buildTypeSafeMap
|
||||||
import net.mamoe.mirai.utils.toLongUnsigned
|
import net.mamoe.mirai.utils.toLongUnsigned
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,68 +155,16 @@ internal object ReceiveMessageTransformer {
|
|||||||
bot: Bot,
|
bot: Bot,
|
||||||
builder: MessageChainBuilder,
|
builder: MessageChainBuilder,
|
||||||
) {
|
) {
|
||||||
// ProtoBuf.encodeToHexString(elements).soutv("join")
|
val pipeline = MessageProtocolFacade.decoderPipeline
|
||||||
// (this._miraiContentToString().soutv())
|
|
||||||
for (element in elements) {
|
val attributes = buildTypeSafeMap {
|
||||||
transformElement(element, groupIdOrZero, messageSourceKind, bot, builder)
|
set(BOT, bot)
|
||||||
when {
|
set(MESSAGE_SOURCE_KIND, messageSourceKind)
|
||||||
element.richMsg != null -> {
|
set(GROUP_ID, groupIdOrZero)
|
||||||
// removed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun transformElement(
|
runCoroutineInPlace {
|
||||||
element: ImMsgBody.Elem,
|
elements.forEach { builder.addAll(pipeline.process(it, attributes)) }
|
||||||
groupIdOrZero: Long,
|
|
||||||
messageSourceKind: MessageSourceKind,
|
|
||||||
bot: Bot,
|
|
||||||
builder: MessageChainBuilder,
|
|
||||||
) {
|
|
||||||
when {
|
|
||||||
element.srcMsg != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.notOnlineImage != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.customFace != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.face != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.text != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.marketFace != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.lightApp != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.customElem != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.commonElem != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
element.transElemInfo != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
element.elemFlags2 != null
|
|
||||||
|| element.extraInfo != null
|
|
||||||
|| element.generalFlags != null
|
|
||||||
|| element.anonGroupMsg != null
|
|
||||||
-> {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// removed
|
|
||||||
// println(it._miraiContentToString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,27 +271,6 @@ internal object ReceiveMessageTransformer {
|
|||||||
return builder.asMessageChain()
|
return builder.asMessageChain()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeText(text: ImMsgBody.Text, list: MessageChainBuilder) {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decodeSrcMsg(
|
|
||||||
srcMsg: ImMsgBody.SourceMsg,
|
|
||||||
list: MessageChainBuilder,
|
|
||||||
bot: Bot,
|
|
||||||
messageSourceKind: MessageSourceKind,
|
|
||||||
groupIdOrZero: Long,
|
|
||||||
) {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decodeLightApp(
|
|
||||||
lightApp: ImMsgBody.LightAppElem,
|
|
||||||
list: MessageChainBuilder,
|
|
||||||
) {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ImMsgBody.Ptt.toAudio() = OnlineAudioImpl(
|
fun ImMsgBody.Ptt.toAudio() = OnlineAudioImpl(
|
||||||
filename = fileName.decodeToString(),
|
filename = fileName.decodeToString(),
|
||||||
fileMd5 = fileMd5,
|
fileMd5 = fileMd5,
|
||||||
|
@ -9,27 +9,16 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message
|
package net.mamoe.mirai.internal.message
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.AnonymousMember
|
|
||||||
import net.mamoe.mirai.contact.ContactOrBot
|
import net.mamoe.mirai.contact.ContactOrBot
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.internal.message.protocol.MessageEncoderContext
|
||||||
import net.mamoe.mirai.internal.message.data.MarketFaceImpl
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||||
import net.mamoe.mirai.internal.message.data.UnsupportedMessageImpl
|
|
||||||
import net.mamoe.mirai.internal.message.flags.InternalFlagOnlyMessage
|
|
||||||
import net.mamoe.mirai.internal.message.image.OfflineFriendImage
|
|
||||||
import net.mamoe.mirai.internal.message.image.OfflineGroupImage
|
|
||||||
import net.mamoe.mirai.internal.message.image.OnlineFriendImageImpl
|
|
||||||
import net.mamoe.mirai.internal.message.image.OnlineGroupImageImpl
|
|
||||||
import net.mamoe.mirai.internal.message.source.MessageSourceInternal
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||||
import net.mamoe.mirai.utils.hexToBytes
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
internal val MIRAI_CUSTOM_ELEM_TYPE = "mirai".hashCode() // 103904510
|
import net.mamoe.mirai.utils.buildTypeSafeMap
|
||||||
|
|
||||||
|
|
||||||
internal val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
|
|
||||||
internal val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。")
|
|
||||||
internal val UNSUPPORTED_FLASH_MESSAGE_PLAIN = PlainText("[闪照]请使用新版手机QQ查看闪照。")
|
|
||||||
internal val UNSUPPORTED_VOICE_MESSAGE_PLAIN = PlainText("收到语音消息,你需要升级到最新版QQ才能接收,升级地址https://im.qq.com")
|
internal val UNSUPPORTED_VOICE_MESSAGE_PLAIN = PlainText("收到语音消息,你需要升级到最新版QQ才能接收,升级地址https://im.qq.com")
|
||||||
|
|
||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
@ -38,138 +27,21 @@ internal fun MessageChain.toRichTextElems(
|
|||||||
withGeneralFlags: Boolean,
|
withGeneralFlags: Boolean,
|
||||||
isForward: Boolean = false,
|
isForward: Boolean = false,
|
||||||
): MutableList<ImMsgBody.Elem> {
|
): MutableList<ImMsgBody.Elem> {
|
||||||
val forGroup = messageTarget is Group
|
val originalMessage = this
|
||||||
val elements = ArrayList<ImMsgBody.Elem>(this.size)
|
val pipeline = MessageProtocolFacade.encoderPipeline
|
||||||
|
|
||||||
var longTextResId: String? = null
|
val attributes = buildTypeSafeMap {
|
||||||
|
set(MessageEncoderContext.CONTACT, messageTarget)
|
||||||
fun transformOneMessage(currentMessage: Message) {
|
set(MessageEncoderContext.ORIGINAL_MESSAGE, originalMessage)
|
||||||
if (currentMessage is RichMessage) {
|
set(MessageEncoderContext.ADD_GENERAL_FLAGS, withGeneralFlags)
|
||||||
// removed
|
set(MessageEncoderContext.IS_FORWARD, isForward)
|
||||||
}
|
|
||||||
|
|
||||||
when (currentMessage) {
|
|
||||||
is PlainText -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is CustomMessage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is At -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is PokeMessage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is OfflineGroupImage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is OnlineGroupImageImpl -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is OnlineFriendImageImpl -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is OfflineFriendImage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is FlashImage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
is AtAll -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is Face -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is QuoteReply -> { // transformed
|
|
||||||
}
|
|
||||||
is Dice -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is MarketFace -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is VipFace -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is PttMessage -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
is MusicShare -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
|
|
||||||
is ForwardMessage,
|
|
||||||
is MessageSource, // mirai metadata only
|
|
||||||
is RichMessage, // already transformed above
|
|
||||||
-> {
|
|
||||||
|
|
||||||
}
|
|
||||||
is InternalFlagOnlyMessage, is ShowImageFlag -> {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
is UnsupportedMessageImpl -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// unrecognized types are ignored
|
|
||||||
// error("unsupported message type: ${currentMessage::class.simpleName}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.anyIsInstance<QuoteReply>()) {
|
val builder = ArrayList<ImMsgBody.Elem>(originalMessage.size)
|
||||||
when (val source = this[QuoteReply]!!.source) {
|
|
||||||
is MessageSourceInternal -> {
|
runCoroutineInPlace {
|
||||||
elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
originalMessage.forEach { builder.addAll(pipeline.process(it, attributes)) }
|
||||||
if (forGroup) {
|
|
||||||
if (source is OnlineMessageSource.Incoming.FromGroup) {
|
|
||||||
val sender0 = source.sender
|
|
||||||
if (sender0 !is AnonymousMember)
|
|
||||||
transformOneMessage(At(sender0))
|
|
||||||
// transformOneMessage(PlainText(" "))
|
|
||||||
// removed by https://github.com/mamoe/mirai/issues/524
|
|
||||||
// 发送 QuoteReply 消息时无可避免的产生多余空格 #524
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}. Don't implement your own MessageSource.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.forEach(::transformOneMessage)
|
return builder
|
||||||
|
|
||||||
if (withGeneralFlags) {
|
|
||||||
when {
|
|
||||||
longTextResId != null -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
this.anyIsInstance<MarketFaceImpl>() -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
this.anyIsInstance<RichMessage>() -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
this.anyIsInstance<FlashImage>() -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
this.anyIsInstance<PttMessage>() -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// removed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
|
||||||
internal val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.internal.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.internal.pipeline.PipelineConsumptionMarker
|
||||||
|
import net.mamoe.mirai.internal.pipeline.Processor
|
||||||
|
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
||||||
|
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
||||||
|
import net.mamoe.mirai.message.data.Message
|
||||||
|
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||||
|
import net.mamoe.mirai.utils.TypeKey
|
||||||
|
import kotlin.coroutines.RestrictsSuspension
|
||||||
|
|
||||||
|
internal interface MessageDecoderPipeline : ProcessorPipeline<MessageDecoderProcessor, ImMsgBody.Elem, Message>
|
||||||
|
|
||||||
|
|
||||||
|
@RestrictsSuspension // Implementor can only call `MessageDecoderContext.process` and `processAlso` so there will be no suspension point
|
||||||
|
internal interface MessageDecoderContext : ProcessorPipelineContext<ImMsgBody.Elem, Message> {
|
||||||
|
companion object {
|
||||||
|
val BOT = TypeKey<Bot>("bot")
|
||||||
|
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
||||||
|
val GROUP_ID = TypeKey<Long>("groupId") // zero if not group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface MessageDecoder : PipelineConsumptionMarker {
|
||||||
|
suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for [MessageDecoder] to be used as [Processor].
|
||||||
|
*/
|
||||||
|
internal class MessageDecoderProcessor(
|
||||||
|
private val decoder: MessageDecoder,
|
||||||
|
) : Processor<MessageDecoderContext, ImMsgBody.Elem> {
|
||||||
|
override suspend fun process(context: MessageDecoderContext, data: ImMsgBody.Elem) {
|
||||||
|
@Suppress("ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL")
|
||||||
|
decoder.run { context.process(data) }
|
||||||
|
// TODO: 2022/4/27 handle exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "MessageDecoderProcessor(decoder=$decoder)"
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.message.protocol
|
|||||||
|
|
||||||
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.pipeline.AbstractProcessorPipeline
|
||||||
|
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import net.mamoe.mirai.utils.TypeSafeMap
|
import net.mamoe.mirai.utils.TypeSafeMap
|
||||||
@ -25,6 +26,7 @@ private val defaultTraceLogging: MiraiLogger by lazy {
|
|||||||
|
|
||||||
internal open class MessageDecoderPipelineImpl :
|
internal open class MessageDecoderPipelineImpl :
|
||||||
AbstractProcessorPipeline<MessageDecoderProcessor, MessageDecoderContext, ImMsgBody.Elem, Message>(
|
AbstractProcessorPipeline<MessageDecoderProcessor, MessageDecoderContext, ImMsgBody.Elem, Message>(
|
||||||
|
PipelineConfiguration(stopWhenConsumed = true),
|
||||||
defaultTraceLogging
|
defaultTraceLogging
|
||||||
),
|
),
|
||||||
MessageDecoderPipeline {
|
MessageDecoderPipeline {
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.contact.ContactOrBot
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.internal.pipeline.PipelineConsumptionMarker
|
||||||
|
import net.mamoe.mirai.internal.pipeline.Processor
|
||||||
|
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
||||||
|
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
||||||
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
|
import net.mamoe.mirai.utils.TypeKey
|
||||||
|
import net.mamoe.mirai.utils.uncheckedCast
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
internal interface MessageEncoderPipeline :
|
||||||
|
ProcessorPipeline<MessageEncoderProcessor<*>, SingleMessage, ImMsgBody.Elem> {
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface MessageEncoderContext : ProcessorPipelineContext<SingleMessage, ImMsgBody.Elem> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General flags that should be appended to the end of the result.
|
||||||
|
*
|
||||||
|
* Do not update this property directly, but call [collectGeneralFlags].
|
||||||
|
*/
|
||||||
|
var generalFlags: ImMsgBody.Elem
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ADD_GENERAL_FLAGS = TypeKey<Boolean>("addGeneralFlags")
|
||||||
|
val MessageEncoderContext.addGeneralFlags get() = attributes[ADD_GENERAL_FLAGS]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override default generalFlags if needed
|
||||||
|
*/
|
||||||
|
inline fun MessageEncoderContext.collectGeneralFlags(block: () -> ImMsgBody.Elem) {
|
||||||
|
if (addGeneralFlags) {
|
||||||
|
generalFlags = block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val CONTACT = TypeKey<ContactOrBot?>("contactOrBot")
|
||||||
|
val MessageEncoderContext.contact get() = attributes[CONTACT]
|
||||||
|
|
||||||
|
val ORIGINAL_MESSAGE = TypeKey<MessageChain>("originalMessage")
|
||||||
|
val MessageEncoderContext.originalMessage get() = attributes[ORIGINAL_MESSAGE]
|
||||||
|
|
||||||
|
val IS_FORWARD = TypeKey<Boolean>("isForward")
|
||||||
|
val MessageEncoderContext.isForward get() = attributes[IS_FORWARD]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun interface MessageEncoder<T : SingleMessage> : PipelineConsumptionMarker {
|
||||||
|
suspend fun MessageEncoderContext.process(data: T)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for [MessageEncoder] to be used as [Processor].
|
||||||
|
*/
|
||||||
|
internal class MessageEncoderProcessor<T : SingleMessage>(
|
||||||
|
private val encoder: MessageEncoder<T>,
|
||||||
|
private val elementType: KClass<T>,
|
||||||
|
) : Processor<MessageEncoderContext, SingleMessage> {
|
||||||
|
override suspend fun process(context: MessageEncoderContext, data: SingleMessage) {
|
||||||
|
if (elementType.isInstance(data)) {
|
||||||
|
encoder.run { context.process(data.uncheckedCast()) }
|
||||||
|
// TODO: 2022/4/27 handle exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol
|
package net.mamoe.mirai.internal.message.protocol
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.PB_RESERVE_FOR_ELSE
|
|
||||||
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.pipeline.AbstractProcessorPipeline
|
||||||
|
import net.mamoe.mirai.internal.pipeline.PipelineConfiguration
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ private val defaultTraceLogging: MiraiLogger by lazy {
|
|||||||
|
|
||||||
internal open class MessageEncoderPipelineImpl :
|
internal open class MessageEncoderPipelineImpl :
|
||||||
AbstractProcessorPipeline<MessageEncoderProcessor<*>, MessageEncoderContext, SingleMessage, ImMsgBody.Elem>(
|
AbstractProcessorPipeline<MessageEncoderProcessor<*>, MessageEncoderContext, SingleMessage, ImMsgBody.Elem>(
|
||||||
|
PipelineConfiguration(stopWhenConsumed = true),
|
||||||
defaultTraceLogging
|
defaultTraceLogging
|
||||||
),
|
),
|
||||||
MessageEncoderPipeline {
|
MessageEncoderPipeline {
|
||||||
@ -34,4 +35,8 @@ internal open class MessageEncoderPipelineImpl :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createContext(attributes: TypeSafeMap): MessageEncoderContext = MessageEncoderContextImpl(attributes)
|
override fun createContext(attributes: TypeSafeMap): MessageEncoderContext = MessageEncoderContextImpl(attributes)
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,33 +9,12 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol
|
package net.mamoe.mirai.internal.message.protocol
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
|
||||||
import net.mamoe.mirai.internal.pipeline.PipelineConsumptionMarker
|
|
||||||
import net.mamoe.mirai.internal.pipeline.Processor
|
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
|
||||||
import net.mamoe.mirai.message.data.Message
|
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
import net.mamoe.mirai.utils.TypeKey
|
|
||||||
import net.mamoe.mirai.utils.uncheckedCast
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
internal abstract class ProcessorCollector {
|
// Loaded by ServiceLoader
|
||||||
inline fun <reified T : SingleMessage> add(encoder: MessageEncoder<T>) = add(encoder, T::class)
|
|
||||||
|
|
||||||
|
|
||||||
abstract fun <T : SingleMessage> add(encoder: MessageEncoder<T>, elementType: KClass<T>)
|
|
||||||
|
|
||||||
abstract fun add(decoder: MessageDecoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract class MessageProtocol(
|
internal abstract class MessageProtocol(
|
||||||
private val priority: UInt = 1000u // the higher, the prior it being called
|
val priority: UInt = PRIORITY_CONTENT // the higher, the prior it being called
|
||||||
) {
|
) {
|
||||||
fun collectProcessors(processorCollector: ProcessorCollector) {
|
fun collectProcessors(processorCollector: ProcessorCollector) {
|
||||||
processorCollector.collectProcessorsImpl()
|
processorCollector.collectProcessorsImpl()
|
||||||
@ -46,124 +25,36 @@ internal abstract class MessageProtocol(
|
|||||||
companion object {
|
companion object {
|
||||||
const val PRIORITY_METADATA: UInt = 10000u
|
const val PRIORITY_METADATA: UInt = 10000u
|
||||||
const val PRIORITY_CONTENT: UInt = 1000u
|
const val PRIORITY_CONTENT: UInt = 1000u
|
||||||
|
const val PRIORITY_IGNORE: UInt = 500u
|
||||||
const val PRIORITY_UNSUPPORTED: UInt = 100u
|
const val PRIORITY_UNSUPPORTED: UInt = 100u
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal object MessageProtocols {
|
object PriorityComparator : Comparator<MessageProtocol> {
|
||||||
val instances: List<MessageProtocol> = initialize()
|
override fun compare(o1: MessageProtocol, o2: MessageProtocol): Int {
|
||||||
|
|
||||||
private fun initialize(): List<MessageProtocol> {
|
// Do not use o1.compareTo
|
||||||
val encoderPipeline = MessageEncoderPipelineImpl()
|
// > Task :mirai-core:checkAndroidApiLevel
|
||||||
val decoderPipeline = MessageDecoderPipelineImpl()
|
// > /Users/runner/work/mirai/mirai/mirai-core/build/classes/kotlin/android/main/net/mamoe/mirai/internal/message/protocol/MessageProtocol$PriorityComparator.class
|
||||||
|
// > Method compare(Lnet/mamoe/mirai/internal/message/protocol/MessageProtocol;Lnet/mamoe/mirai/internal/message/protocol/MessageProtocol;)I
|
||||||
|
// > Invoke method java/lang/Integer.compareUnsigned(II)I
|
||||||
|
// Couldn't access java/lang/Integer.compareUnsigned(II)I: java/lang/Integer.compareUnsigned(II)I since api level 26
|
||||||
|
|
||||||
val instances = ServiceLoader.load(MessageProtocol::class.java).iterator().asSequence().toList()
|
return uintCompare(o1.priority.toInt(), o2.priority.toInt())
|
||||||
for (instance in instances) {
|
|
||||||
instance.collectProcessors(object : ProcessorCollector() {
|
|
||||||
override fun <T : SingleMessage> add(encoder: MessageEncoder<T>, elementType: KClass<T>) {
|
|
||||||
encoderPipeline.registerProcessor(MessageEncoderProcessor(encoder, elementType))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun add(decoder: MessageDecoder) {
|
|
||||||
decoderPipeline.registerProcessor(MessageDecoderProcessor(decoder))
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return instances
|
private fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// decoders
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
internal interface MessageDecoderContext : ProcessorPipelineContext<ImMsgBody.Elem, Message> {
|
|
||||||
companion object {
|
|
||||||
val BOT = TypeKey<Bot>("bot")
|
|
||||||
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
|
||||||
val GROUP_ID = TypeKey<Long>("groupId") // zero if not group
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface MessageDecoder : PipelineConsumptionMarker {
|
internal abstract class ProcessorCollector {
|
||||||
suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem)
|
inline fun <reified T : SingleMessage> add(encoder: MessageEncoder<T>) = add(encoder, T::class)
|
||||||
|
|
||||||
|
|
||||||
|
abstract fun <T : SingleMessage> add(encoder: MessageEncoder<T>, elementType: KClass<T>)
|
||||||
|
|
||||||
|
abstract fun add(decoder: MessageDecoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter for [MessageDecoder] to be used as [Processor].
|
|
||||||
*/
|
|
||||||
internal class MessageDecoderProcessor(
|
|
||||||
private val decoder: MessageDecoder
|
|
||||||
) : Processor<MessageDecoderContext, ImMsgBody.Elem> {
|
|
||||||
override suspend fun process(context: MessageDecoderContext, data: ImMsgBody.Elem) {
|
|
||||||
decoder.run { context.process(data) }
|
|
||||||
// TODO: 2022/4/27 handle exceptions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface MessageDecoderPipeline : ProcessorPipeline<MessageDecoderProcessor, ImMsgBody.Elem, Message>
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// encoders
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
internal interface MessageEncoderContext : ProcessorPipelineContext<SingleMessage, ImMsgBody.Elem> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* General flags that should be appended to the end of the result.
|
|
||||||
*
|
|
||||||
* Do not update this property directly, but call [collectGeneralFlags].
|
|
||||||
*/
|
|
||||||
var generalFlags: ImMsgBody.Elem
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val ADD_GENERAL_FLAGS = TypeKey<Boolean>("addGeneralFlags")
|
|
||||||
val MessageEncoderContext.addGeneralFlags get() = attributes[ADD_GENERAL_FLAGS]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override default generalFlags if needed
|
|
||||||
*/
|
|
||||||
inline fun MessageEncoderContext.collectGeneralFlags(block: () -> ImMsgBody.Elem) {
|
|
||||||
if (addGeneralFlags) {
|
|
||||||
generalFlags = block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val CONTACT = TypeKey<Contact>("contact")
|
|
||||||
val MessageEncoderContext.contact get() = attributes[CONTACT]
|
|
||||||
|
|
||||||
val ORIGINAL_MESSAGE = TypeKey<MessageChain>("originalMessage")
|
|
||||||
val MessageEncoderContext.originalMessage get() = attributes[ORIGINAL_MESSAGE]
|
|
||||||
|
|
||||||
val IS_FORWARD = TypeKey<Boolean>("isForward")
|
|
||||||
val MessageEncoderContext.isForward get() = attributes[IS_FORWARD]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun interface MessageEncoder<T : SingleMessage> : PipelineConsumptionMarker {
|
|
||||||
suspend fun MessageEncoderContext.process(data: T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter for [MessageEncoder] to be used as [Processor].
|
|
||||||
*/
|
|
||||||
internal class MessageEncoderProcessor<T : SingleMessage>(
|
|
||||||
private val encoder: MessageEncoder<T>,
|
|
||||||
private val elementType: KClass<T>,
|
|
||||||
) : Processor<MessageEncoderContext, SingleMessage> {
|
|
||||||
override suspend fun process(context: MessageEncoderContext, data: SingleMessage) {
|
|
||||||
if (elementType.isInstance(data)) {
|
|
||||||
encoder.run { context.process(data.uncheckedCast()) }
|
|
||||||
// TODO: 2022/4/27 handle exceptions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface MessageEncoderPipeline : ProcessorPipeline<MessageEncoderProcessor<*>, SingleMessage, ImMsgBody.Elem>
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// refiners
|
// refiners
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.message.data.SingleMessage
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
internal interface MessageProtocolFacade {
|
||||||
|
val encoderPipeline: MessageEncoderPipeline
|
||||||
|
val decoderPipeline: MessageDecoderPipeline
|
||||||
|
val loaded: List<MessageProtocol>
|
||||||
|
|
||||||
|
companion object INSTANCE : MessageProtocolFacade by MessageProtocolFacadeImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageProtocolFacadeImpl : MessageProtocolFacade {
|
||||||
|
override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl()
|
||||||
|
override val decoderPipeline: MessageDecoderPipeline = MessageDecoderPipelineImpl()
|
||||||
|
|
||||||
|
override val loaded: List<MessageProtocol> = initialize()
|
||||||
|
|
||||||
|
private fun initialize(): List<MessageProtocol> {
|
||||||
|
val instances = ServiceLoader.load(MessageProtocol::class.java).iterator().asSequence()
|
||||||
|
.toCollection(PriorityQueue(MessageProtocol.PriorityComparator.reversed()))
|
||||||
|
|
||||||
|
for (instance in instances) {
|
||||||
|
instance.collectProcessors(object : ProcessorCollector() {
|
||||||
|
override fun <T : SingleMessage> add(encoder: MessageEncoder<T>, elementType: KClass<T>) {
|
||||||
|
encoderPipeline.registerProcessor(MessageEncoderProcessor(encoder, elementType))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(decoder: MessageDecoder) {
|
||||||
|
decoderPipeline.registerProcessor(MessageDecoderProcessor(decoder))
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return instances.toList()
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.MIRAI_CUSTOM_ELEM_TYPE
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.*
|
import net.mamoe.mirai.internal.message.protocol.*
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.message.data.CustomMessage
|
import net.mamoe.mirai.message.data.CustomMessage
|
||||||
@ -24,6 +23,8 @@ internal class CustomMessageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<CustomMessage> {
|
private class Encoder : MessageEncoder<CustomMessage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: CustomMessage) {
|
override suspend fun MessageEncoderContext.process(data: CustomMessage) {
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
collect(
|
collect(
|
||||||
ImMsgBody.Elem(
|
ImMsgBody.Elem(
|
||||||
@ -37,12 +38,16 @@ internal class CustomMessageProtocol : MessageProtocol() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private val MIRAI_CUSTOM_ELEM_TYPE = "mirai".hashCode() // 103904510
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
if (data.customElem == null) return
|
if (data.customElem == null) return
|
||||||
|
markAsConsumed()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
data.customElem.data.read {
|
data.customElem.data.read {
|
||||||
CustomMessage.load(this)
|
CustomMessage.load(this)
|
||||||
|
@ -28,6 +28,7 @@ internal class FaceProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<Face> {
|
private class Encoder : MessageEncoder<Face> {
|
||||||
override suspend fun MessageEncoderContext.process(data: Face) {
|
override suspend fun MessageEncoderContext.process(data: Face) {
|
||||||
|
markAsConsumed()
|
||||||
collect(
|
collect(
|
||||||
if (data.id >= 260) {
|
if (data.id >= 260) {
|
||||||
ImMsgBody.Elem(commonElem = data.toCommData())
|
ImMsgBody.Elem(commonElem = data.toCommData())
|
||||||
@ -67,6 +68,7 @@ internal class FaceProtocol : MessageProtocol() {
|
|||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
val commonElem = data.commonElem ?: return
|
val commonElem = data.commonElem ?: return
|
||||||
if (commonElem.serviceType != 33) return
|
if (commonElem.serviceType != 33) return
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val proto =
|
val proto =
|
||||||
commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype33.serializer())
|
commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype33.serializer())
|
||||||
|
@ -31,6 +31,11 @@ internal class FileMessageProtocol : MessageProtocol() {
|
|||||||
if (data.transElemInfo == null) return
|
if (data.transElemInfo == null) return
|
||||||
if (data.transElemInfo.elemType != 24) return
|
if (data.transElemInfo.elemType != 24) return
|
||||||
|
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
|
processAlso(data)
|
||||||
|
process(data)
|
||||||
|
|
||||||
data.transElemInfo.elemValue.read {
|
data.transElemInfo.elemValue.read {
|
||||||
// group file feed
|
// group file feed
|
||||||
// 01 00 77 08 06 12 0A 61 61 61 61 61 61 2E 74 78 74 1A 06 31 35 42 79 74 65 3A 5F 12 5D 08 66 12 25 2F 64 37 34 62 62 66 33 61 2D 37 62 32 35 2D 31 31 65 62 2D 38 34 66 38 2D 35 34 35 32 30 30 37 62 35 64 39 66 18 0F 22 0A 61 61 61 61 61 61 2E 74 78 74 28 00 3A 00 42 20 61 33 32 35 66 36 33 34 33 30 65 37 61 30 31 31 66 37 64 30 38 37 66 63 33 32 34 37 35 34 39 63
|
// 01 00 77 08 06 12 0A 61 61 61 61 61 61 2E 74 78 74 1A 06 31 35 42 79 74 65 3A 5F 12 5D 08 66 12 25 2F 64 37 34 62 62 66 33 61 2D 37 62 32 35 2D 31 31 65 62 2D 38 34 66 38 2D 35 34 35 32 30 30 37 62 35 64 39 66 18 0F 22 0A 61 61 61 61 61 61 2E 74 78 74 28 00 3A 00 42 20 61 33 32 35 66 36 33 34 33 30 65 37 61 30 31 31 66 37 64 30 38 37 66 63 33 32 34 37 35 34 39 63
|
||||||
|
@ -11,7 +11,6 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
|||||||
|
|
||||||
import net.mamoe.mirai.contact.ContactOrBot
|
import net.mamoe.mirai.contact.ContactOrBot
|
||||||
import net.mamoe.mirai.contact.User
|
import net.mamoe.mirai.contact.User
|
||||||
import net.mamoe.mirai.internal.message.UNSUPPORTED_FLASH_MESSAGE_PLAIN
|
|
||||||
import net.mamoe.mirai.internal.message.image.OnlineFriendImageImpl
|
import net.mamoe.mirai.internal.message.image.OnlineFriendImageImpl
|
||||||
import net.mamoe.mirai.internal.message.image.OnlineGroupImageImpl
|
import net.mamoe.mirai.internal.message.image.OnlineGroupImageImpl
|
||||||
import net.mamoe.mirai.internal.message.image.friendImageId
|
import net.mamoe.mirai.internal.message.image.friendImageId
|
||||||
@ -23,6 +22,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
|||||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||||
import net.mamoe.mirai.message.data.FlashImage
|
import net.mamoe.mirai.message.data.FlashImage
|
||||||
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
import net.mamoe.mirai.utils.hexToBytes
|
import net.mamoe.mirai.utils.hexToBytes
|
||||||
|
|
||||||
internal class FlashImageProtocol : MessageProtocol() {
|
internal class FlashImageProtocol : MessageProtocol() {
|
||||||
@ -36,6 +36,8 @@ internal class FlashImageProtocol : MessageProtocol() {
|
|||||||
if (data.commonElem == null) return
|
if (data.commonElem == null) return
|
||||||
if (data.commonElem.serviceType != 3) return
|
if (data.commonElem.serviceType != 3) return
|
||||||
|
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val proto =
|
val proto =
|
||||||
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
|
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
|
||||||
if (proto.flashTroopPic != null) {
|
if (proto.flashTroopPic != null) {
|
||||||
@ -51,6 +53,8 @@ internal class FlashImageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<FlashImage> {
|
private class Encoder : MessageEncoder<FlashImage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: FlashImage) {
|
override suspend fun MessageEncoderContext.process(data: FlashImage) {
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
collect(data.toJceData(contact))
|
collect(data.toJceData(contact))
|
||||||
processAlso(UNSUPPORTED_FLASH_MESSAGE_PLAIN)
|
processAlso(UNSUPPORTED_FLASH_MESSAGE_PLAIN)
|
||||||
collectGeneralFlags {
|
collectGeneralFlags {
|
||||||
@ -61,6 +65,7 @@ internal class FlashImageProtocol : MessageProtocol() {
|
|||||||
private companion object {
|
private companion object {
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
private val PB_RESERVE_FOR_DOUTU = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()
|
private val PB_RESERVE_FOR_DOUTU = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()
|
||||||
|
private val UNSUPPORTED_FLASH_MESSAGE_PLAIN = PlainText("[闪照]请使用新版手机QQ查看闪照。")
|
||||||
|
|
||||||
private fun FlashImage.toJceData(messageTarget: ContactOrBot?): ImMsgBody.Elem {
|
private fun FlashImage.toJceData(messageTarget: ContactOrBot?): ImMsgBody.Elem {
|
||||||
return if (messageTarget is User) {
|
return if (messageTarget is User) {
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageEncoder
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageEncoderContext
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
|
||||||
|
|
||||||
internal class GeneralFlagsProtocol : MessageProtocol() {
|
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
|
||||||
add(Encoder())
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Encoder : MessageEncoder<SingleMessage> {
|
|
||||||
override suspend fun MessageEncoderContext.process(data: SingleMessage) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,18 +17,24 @@ import net.mamoe.mirai.message.data.MessageSource
|
|||||||
import net.mamoe.mirai.message.data.ShowImageFlag
|
import net.mamoe.mirai.message.data.ShowImageFlag
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
|
|
||||||
internal class IgnoredMessagesProtocol : MessageProtocol() {
|
internal class IgnoredMessagesProtocol : MessageProtocol(PRIORITY_IGNORE) {
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
add(Encoder())
|
add(Encoder())
|
||||||
add(Decoder())
|
add(Decoder())
|
||||||
|
|
||||||
|
// 所有未处理的 Elem 都会变成 UnsupportedMessage 所有不用在这里处理
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
when (data) {
|
when {
|
||||||
|
data.elemFlags2 != null
|
||||||
|
|| data.extraInfo != null
|
||||||
|
|| data.generalFlags != null
|
||||||
|
|| data.anonGroupMsg != null
|
||||||
|
-> markAsConsumed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Encoder : MessageEncoder<SingleMessage> {
|
private class Encoder : MessageEncoder<SingleMessage> {
|
||||||
|
@ -29,15 +29,22 @@ internal class ImageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class ImageDecoder : MessageDecoder {
|
private class ImageDecoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
if (data.notOnlineImage != null) collect(OnlineFriendImageImpl(data.notOnlineImage))
|
markAsConsumed()
|
||||||
if (data.customFace != null) {
|
when {
|
||||||
collect(OnlineGroupImageImpl(data.customFace))
|
data.notOnlineImage != null -> {
|
||||||
data.customFace.pbReserve.let {
|
collect(OnlineFriendImageImpl(data.notOnlineImage))
|
||||||
if (it.isNotEmpty() && it.loadAs(CustomFace.ResvAttr.serializer()).msgImageShow != null) {
|
}
|
||||||
collect(ShowImageFlag)
|
data.customFace != null -> {
|
||||||
|
collect(OnlineGroupImageImpl(data.customFace))
|
||||||
|
data.customFace.pbReserve.let {
|
||||||
|
if (it.isNotEmpty() && it.loadAs(CustomFace.ResvAttr.serializer()).msgImageShow != null) {
|
||||||
|
collect(ShowImageFlag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
markNotConsumed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +52,8 @@ internal class ImageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class ImageEncoder : MessageEncoder<AbstractImage> {
|
private class ImageEncoder : MessageEncoder<AbstractImage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: AbstractImage) {
|
override suspend fun MessageEncoderContext.process(data: AbstractImage) {
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
when (data) {
|
when (data) {
|
||||||
is OfflineGroupImage -> {
|
is OfflineGroupImage -> {
|
||||||
if (contact is User) {
|
if (contact is User) {
|
||||||
|
@ -7,24 +7,6 @@
|
|||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.data.MarketFaceImpl
|
import net.mamoe.mirai.internal.message.data.MarketFaceImpl
|
||||||
@ -62,6 +44,7 @@ internal class MarketFaceProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class DiceEncoder : MessageEncoder<Dice> {
|
private class DiceEncoder : MessageEncoder<Dice> {
|
||||||
override suspend fun MessageEncoderContext.process(data: Dice) {
|
override suspend fun MessageEncoderContext.process(data: Dice) {
|
||||||
|
markAsConsumed()
|
||||||
processAlso(MarketFaceImpl(data.toJceStruct()))
|
processAlso(MarketFaceImpl(data.toJceStruct()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,13 @@ import net.mamoe.mirai.message.data.content
|
|||||||
|
|
||||||
internal class MusicShareProtocol : MessageProtocol() {
|
internal class MusicShareProtocol : MessageProtocol() {
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
|
add(Encoder())
|
||||||
|
// add(Decoder())
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Encoder : MessageEncoder<MusicShare> {
|
private class Encoder : MessageEncoder<MusicShare> {
|
||||||
override suspend fun MessageEncoderContext.process(data: MusicShare) {
|
override suspend fun MessageEncoderContext.process(data: MusicShare) {
|
||||||
|
markAsConsumed()
|
||||||
// 只有在 QuoteReply 的 source 里才会进行 MusicShare 转换, 因此可以转 PT.
|
// 只有在 QuoteReply 的 source 里才会进行 MusicShare 转换, 因此可以转 PT.
|
||||||
// 发送消息时会被特殊处理
|
// 发送消息时会被特殊处理
|
||||||
processAlso(PlainText(data.content))
|
processAlso(PlainText(data.content))
|
||||||
|
@ -9,15 +9,19 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import net.mamoe.mirai.internal.message.UNSUPPORTED_POKE_MESSAGE_PLAIN
|
|
||||||
import net.mamoe.mirai.internal.message.protocol.*
|
import net.mamoe.mirai.internal.message.protocol.*
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
|
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
|
||||||
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.utils.io.serialization.loadAs
|
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||||
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
import net.mamoe.mirai.message.data.PokeMessage
|
import net.mamoe.mirai.message.data.PokeMessage
|
||||||
|
|
||||||
internal class PokeMessageProtocol : MessageProtocol() {
|
internal class PokeMessageProtocol : MessageProtocol() {
|
||||||
|
companion object {
|
||||||
|
val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。")
|
||||||
|
}
|
||||||
|
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
add(Encoder())
|
add(Encoder())
|
||||||
add(Decoder())
|
add(Decoder())
|
||||||
@ -25,6 +29,7 @@ internal class PokeMessageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<PokeMessage> {
|
private class Encoder : MessageEncoder<PokeMessage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: PokeMessage) {
|
override suspend fun MessageEncoderContext.process(data: PokeMessage) {
|
||||||
|
markAsConsumed()
|
||||||
collect(
|
collect(
|
||||||
ImMsgBody.Elem(
|
ImMsgBody.Elem(
|
||||||
commonElem = ImMsgBody.CommonElem(
|
commonElem = ImMsgBody.CommonElem(
|
||||||
@ -41,13 +46,13 @@ internal class PokeMessageProtocol : MessageProtocol() {
|
|||||||
)
|
)
|
||||||
processAlso(UNSUPPORTED_POKE_MESSAGE_PLAIN)
|
processAlso(UNSUPPORTED_POKE_MESSAGE_PLAIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
if (data.commonElem == null) return
|
if (data.commonElem == null) return
|
||||||
if (data.commonElem.serviceType != 2) return
|
if (data.commonElem.serviceType != 2) return
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val proto =
|
val proto =
|
||||||
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype2.serializer())
|
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype2.serializer())
|
||||||
|
@ -26,6 +26,7 @@ internal class PttMessageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<PttMessage> {
|
private class Encoder : MessageEncoder<PttMessage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: PttMessage) {
|
override suspend fun MessageEncoderContext.process(data: PttMessage) {
|
||||||
|
markAsConsumed()
|
||||||
collect(
|
collect(
|
||||||
ImMsgBody.Elem(
|
ImMsgBody.Elem(
|
||||||
extraInfo = ImMsgBody.ExtraInfo(flags = 16, groupMask = 1)
|
extraInfo = ImMsgBody.ExtraInfo(flags = 16, groupMask = 1)
|
||||||
|
@ -32,11 +32,16 @@ internal class QuoteReplyProtocol : MessageProtocol(PRIORITY_METADATA) {
|
|||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
if (data.srcMsg == null) return
|
if (data.srcMsg == null) return
|
||||||
OfflineMessageSourceImplData(
|
markAsConsumed()
|
||||||
data.srcMsg,
|
collect(
|
||||||
attributes[BOT],
|
QuoteReply(
|
||||||
attributes[MESSAGE_SOURCE_KIND],
|
OfflineMessageSourceImplData(
|
||||||
attributes[GROUP_ID]
|
data.srcMsg,
|
||||||
|
attributes[BOT],
|
||||||
|
attributes[MESSAGE_SOURCE_KIND],
|
||||||
|
attributes[GROUP_ID]
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +50,7 @@ internal class QuoteReplyProtocol : MessageProtocol(PRIORITY_METADATA) {
|
|||||||
private class Encoder : MessageEncoder<QuoteReply> {
|
private class Encoder : MessageEncoder<QuoteReply> {
|
||||||
override suspend fun MessageEncoderContext.process(data: QuoteReply) {
|
override suspend fun MessageEncoderContext.process(data: QuoteReply) {
|
||||||
val source = data.source as? MessageSourceInternal ?: return
|
val source = data.source as? MessageSourceInternal ?: return
|
||||||
|
markAsConsumed()
|
||||||
collect(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
collect(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||||
if (contact is Group) {
|
if (contact is Group) {
|
||||||
if (source is OnlineMessageSource.Incoming.FromGroup) {
|
if (source is OnlineMessageSource.Incoming.FromGroup) {
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
import kotlinx.io.core.toByteArray
|
import kotlinx.io.core.toByteArray
|
||||||
import net.mamoe.mirai.internal.message.UNSUPPORTED_MERGED_MESSAGE_PLAIN
|
|
||||||
import net.mamoe.mirai.internal.message.data.ForwardMessageInternal
|
import net.mamoe.mirai.internal.message.data.ForwardMessageInternal
|
||||||
import net.mamoe.mirai.internal.message.data.LightAppInternal
|
import net.mamoe.mirai.internal.message.data.LightAppInternal
|
||||||
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
||||||
@ -32,6 +31,10 @@ import net.mamoe.mirai.utils.zip
|
|||||||
* - [ForwardMessage]
|
* - [ForwardMessage]
|
||||||
*/
|
*/
|
||||||
internal class RichMessageProtocol : MessageProtocol() {
|
internal class RichMessageProtocol : MessageProtocol() {
|
||||||
|
companion object {
|
||||||
|
val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
|
||||||
|
}
|
||||||
|
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
add(RichMsgDecoder())
|
add(RichMsgDecoder())
|
||||||
add(LightAppDecoder())
|
add(LightAppDecoder())
|
||||||
@ -41,6 +44,7 @@ internal class RichMessageProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<RichMessage> {
|
private class Encoder : MessageEncoder<RichMessage> {
|
||||||
override suspend fun MessageEncoderContext.process(data: RichMessage) {
|
override suspend fun MessageEncoderContext.process(data: RichMessage) {
|
||||||
|
markAsConsumed()
|
||||||
val content = data.content.toByteArray().zip()
|
val content = data.content.toByteArray().zip()
|
||||||
var longTextResId: String? = null
|
var longTextResId: String? = null
|
||||||
when (data) {
|
when (data) {
|
||||||
@ -92,7 +96,7 @@ internal class RichMessageProtocol : MessageProtocol() {
|
|||||||
ImMsgBody.Elem(
|
ImMsgBody.Elem(
|
||||||
generalFlags = ImMsgBody.GeneralFlags(
|
generalFlags = ImMsgBody.GeneralFlags(
|
||||||
longTextFlag = 1,
|
longTextFlag = 1,
|
||||||
longTextResid = longTextResId!!,
|
longTextResid = longTextResId,
|
||||||
pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -112,6 +116,7 @@ internal class RichMessageProtocol : MessageProtocol() {
|
|||||||
private class LightAppDecoder : MessageDecoder {
|
private class LightAppDecoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
val lightApp = data.lightApp ?: return
|
val lightApp = data.lightApp ?: return
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val content = runWithBugReport("解析 lightApp",
|
val content = runWithBugReport("解析 lightApp",
|
||||||
{ "resId=" + lightApp.msgResid + "data=" + lightApp.data.toUHexString() }) {
|
{ "resId=" + lightApp.msgResid + "data=" + lightApp.data.toUHexString() }) {
|
||||||
|
@ -40,6 +40,7 @@ internal class TextProtocol : MessageProtocol() {
|
|||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
val text = data.text ?: return
|
val text = data.text ?: return
|
||||||
|
markAsConsumed()
|
||||||
if (text.attr6Buf.isEmpty()) {
|
if (text.attr6Buf.isEmpty()) {
|
||||||
collect(PlainText(text.str))
|
collect(PlainText(text.str))
|
||||||
} else {
|
} else {
|
||||||
@ -59,16 +60,18 @@ internal class TextProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class PlainTextEncoder : MessageEncoder<PlainText> {
|
private class PlainTextEncoder : MessageEncoder<PlainText> {
|
||||||
override suspend fun MessageEncoderContext.process(data: PlainText) {
|
override suspend fun MessageEncoderContext.process(data: PlainText) {
|
||||||
|
markAsConsumed()
|
||||||
collect(ImMsgBody.Elem(text = ImMsgBody.Text(str = data.content)))
|
collect(ImMsgBody.Elem(text = ImMsgBody.Text(str = data.content)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AtEncoder : MessageEncoder<At> {
|
private class AtEncoder : MessageEncoder<At> {
|
||||||
override suspend fun MessageEncoderContext.process(data: At) {
|
override suspend fun MessageEncoderContext.process(data: At) {
|
||||||
|
markAsConsumed()
|
||||||
collected += ImMsgBody.Elem(
|
collected += ImMsgBody.Elem(
|
||||||
text = data.toJceData(
|
text = data.toJceData(
|
||||||
attributes[CONTACT].safeCast(),
|
attributes[CONTACT].safeCast(),
|
||||||
originalMessage[MessageSource],
|
originalMessage.sourceOrNull,
|
||||||
isForward,
|
isForward,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -148,6 +151,7 @@ internal class TextProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class AtAllEncoder : MessageEncoder<AtAll> {
|
private class AtAllEncoder : MessageEncoder<AtAll> {
|
||||||
override suspend fun MessageEncoderContext.process(data: AtAll) {
|
override suspend fun MessageEncoderContext.process(data: AtAll) {
|
||||||
|
markAsConsumed()
|
||||||
collect(jceData)
|
collect(jceData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import net.mamoe.mirai.internal.message.data.UnsupportedMessageImpl
|
|||||||
import net.mamoe.mirai.internal.message.protocol.*
|
import net.mamoe.mirai.internal.message.protocol.*
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
|
|
||||||
internal class UnsupportedMessageProtocol : MessageProtocol(priority = 100u) {
|
internal class UnsupportedMessageProtocol : MessageProtocol(priority = PRIORITY_UNSUPPORTED) {
|
||||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||||
add(Decoder())
|
add(Decoder())
|
||||||
add(Encoder())
|
add(Encoder())
|
||||||
@ -21,13 +21,16 @@ internal class UnsupportedMessageProtocol : MessageProtocol(priority = 100u) {
|
|||||||
|
|
||||||
private class Decoder : MessageDecoder {
|
private class Decoder : MessageDecoder {
|
||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
val struct = UnsupportedMessageImpl(data).takeIf { it.struct.isNotEmpty() } ?: return
|
markAsConsumed()
|
||||||
|
val struct = UnsupportedMessageImpl(data)
|
||||||
|
if (struct.struct.isEmpty()) return
|
||||||
collect(struct)
|
collect(struct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Encoder : MessageEncoder<UnsupportedMessageImpl> {
|
private class Encoder : MessageEncoder<UnsupportedMessageImpl> {
|
||||||
override suspend fun MessageEncoderContext.process(data: UnsupportedMessageImpl) {
|
override suspend fun MessageEncoderContext.process(data: UnsupportedMessageImpl) {
|
||||||
|
markAsConsumed()
|
||||||
collect(data.structElem)
|
collect(data.structElem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ internal class VipFaceProtocol : MessageProtocol() {
|
|||||||
|
|
||||||
private class Encoder : MessageEncoder<VipFace> {
|
private class Encoder : MessageEncoder<VipFace> {
|
||||||
override suspend fun MessageEncoderContext.process(data: VipFace) {
|
override suspend fun MessageEncoderContext.process(data: VipFace) {
|
||||||
|
markAsConsumed()
|
||||||
processAlso(PlainText(data.contentToString()))
|
processAlso(PlainText(data.contentToString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,6 +33,7 @@ internal class VipFaceProtocol : MessageProtocol() {
|
|||||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
if (data.commonElem == null) return
|
if (data.commonElem == null) return
|
||||||
if (data.commonElem.serviceType != 23) return
|
if (data.commonElem.serviceType != 23) return
|
||||||
|
markAsConsumed()
|
||||||
|
|
||||||
val proto =
|
val proto =
|
||||||
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype23.serializer())
|
data.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype23.serializer())
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 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.
|
* 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/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.network.component
|
package net.mamoe.mirai.internal.network.component
|
||||||
|
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
import kotlin.reflect.full.allSupertypes
|
import kotlin.reflect.full.allSupertypes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A key for specific component [T]. Components are not polymorphic.
|
* A key for specific component [T]. Components are not polymorphic.
|
||||||
*
|
*
|
||||||
|
* Most components locate in `net.mamoe.mirai.internal.network.components` while some like [MessageProtocolFacade] don't.
|
||||||
|
*
|
||||||
* @param T is a type hint.
|
* @param T is a type hint.
|
||||||
*/
|
*/
|
||||||
internal interface ComponentKey<T : Any> {
|
internal interface ComponentKey<T : Any> {
|
||||||
|
@ -28,10 +28,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg
|
|||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetMsg
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetMsg
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushPbPushTransMsg
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushPbPushTransMsg
|
||||||
import net.mamoe.mirai.internal.network.toPacket
|
import net.mamoe.mirai.internal.network.toPacket
|
||||||
import net.mamoe.mirai.internal.pipeline.AbstractProcessorPipeline
|
import net.mamoe.mirai.internal.pipeline.*
|
||||||
import net.mamoe.mirai.internal.pipeline.Processor
|
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipeline
|
|
||||||
import net.mamoe.mirai.internal.pipeline.ProcessorPipelineContext
|
|
||||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -82,7 +79,9 @@ internal open class NoticeProcessorPipelineImpl protected constructor(
|
|||||||
private val bot: QQAndroidBot,
|
private val bot: QQAndroidBot,
|
||||||
traceLogging: MiraiLogger = defaultTraceLogging,
|
traceLogging: MiraiLogger = defaultTraceLogging,
|
||||||
) : NoticeProcessorPipeline,
|
) : NoticeProcessorPipeline,
|
||||||
AbstractProcessorPipeline<NoticeProcessor, NoticePipelineContext, ProtocolStruct, Packet>(traceLogging) {
|
AbstractProcessorPipeline<NoticeProcessor, NoticePipelineContext, ProtocolStruct, Packet>(
|
||||||
|
PipelineConfiguration(stopWhenConsumed = false), traceLogging
|
||||||
|
) {
|
||||||
|
|
||||||
open inner class ContextImpl(
|
open inner class ContextImpl(
|
||||||
attributes: TypeSafeMap,
|
attributes: TypeSafeMap,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 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.
|
* 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/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.network.protocol.data.proto
|
package net.mamoe.mirai.internal.network.protocol.data.proto
|
||||||
@ -14,6 +14,7 @@ import kotlinx.serialization.protobuf.ProtoIntegerType
|
|||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
import kotlinx.serialization.protobuf.ProtoType
|
import kotlinx.serialization.protobuf.ProtoType
|
||||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
||||||
|
import net.mamoe.mirai.internal.utils.structureToString
|
||||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -380,7 +381,11 @@ internal class ImMsgBody : ProtoBuf {
|
|||||||
@ProtoNumber(51) @JvmField val lightApp: LightAppElem? = null,
|
@ProtoNumber(51) @JvmField val lightApp: LightAppElem? = null,
|
||||||
@ProtoNumber(52) @JvmField val eimInfo: EIMInfo? = null,
|
@ProtoNumber(52) @JvmField val eimInfo: EIMInfo? = null,
|
||||||
@ProtoNumber(53) @JvmField val commonElem: CommonElem? = null,
|
@ProtoNumber(53) @JvmField val commonElem: CommonElem? = null,
|
||||||
) : ProtoBuf
|
) : ProtoBuf {
|
||||||
|
override fun toString(): String {
|
||||||
|
return this.structureToString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class ElemFlags(
|
internal class ElemFlags(
|
||||||
|
@ -49,6 +49,10 @@ internal value class MutableProcessResult<R>(
|
|||||||
internal interface PipelineConsumptionMarker
|
internal interface PipelineConsumptionMarker
|
||||||
|
|
||||||
internal interface ProcessorPipelineContext<D, R> {
|
internal interface ProcessorPipelineContext<D, R> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child processes ([processAlso]) will inherit [attributes] from its parent, while any other properties from the context will not.
|
||||||
|
*/
|
||||||
val attributes: TypeSafeMap
|
val attributes: TypeSafeMap
|
||||||
|
|
||||||
val collected: MutableProcessResult<R>
|
val collected: MutableProcessResult<R>
|
||||||
@ -135,11 +139,16 @@ internal abstract class AbstractProcessorPipelineContext<D, R>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class PipelineConfiguration(
|
||||||
|
var stopWhenConsumed: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
internal abstract class AbstractProcessorPipeline<P : Processor<C, D>, C : ProcessorPipelineContext<D, R>, D, R>
|
internal abstract class AbstractProcessorPipeline<P : Processor<C, D>, C : ProcessorPipelineContext<D, R>, D, R>
|
||||||
protected constructor(
|
protected constructor(
|
||||||
|
val configuration: PipelineConfiguration,
|
||||||
val traceLogging: MiraiLogger,
|
val traceLogging: MiraiLogger,
|
||||||
) : ProcessorPipeline<P, D, R> {
|
) : ProcessorPipeline<P, D, R> {
|
||||||
constructor() : this(SilentLogger)
|
constructor(configuration: PipelineConfiguration) : this(configuration, SilentLogger)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must be ordered
|
* Must be ordered
|
||||||
@ -199,6 +208,12 @@ protected constructor(
|
|||||||
}, success=${result.isSuccess}, consumed=${context.isConsumed}, diff=$diffPackets"
|
}, success=${result.isSuccess}, consumed=${context.isConsumed}, diff=$diffPackets"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.isConsumed && configuration.stopWhenConsumed) {
|
||||||
|
traceLogging.info { "stopWhenConsumed=true, stopped." }
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return context.collected.data
|
return context.collected.data
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||||
|
import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the [coroutine] directly in current thread, **expecting no suspension**.
|
||||||
|
*/
|
||||||
|
internal fun <R> runCoroutineInPlace(coroutine: suspend () -> R): R {
|
||||||
|
var lateResult: CompletableDeferred<R>? = null
|
||||||
|
|
||||||
|
val result = coroutine.startCoroutineUninterceptedOrReturn(Continuation(EmptyCoroutineContext) { r ->
|
||||||
|
val deferred: CompletableDeferred<R>? = lateResult
|
||||||
|
@Suppress("KotlinConstantConditions")
|
||||||
|
if (deferred != null) {
|
||||||
|
r.fold(onSuccess = { deferred.complete(it) }, onFailure = { deferred.completeExceptionally(it) })
|
||||||
|
} else {
|
||||||
|
if (logger.isErrorEnabled) {
|
||||||
|
logger.error(IllegalStateException("runCoroutineInPlace reached an unexpected state: coroutine did not finish. ()"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result != COROUTINE_SUSPENDED) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return result as R
|
||||||
|
}
|
||||||
|
|
||||||
|
lateResult = CompletableDeferred()
|
||||||
|
|
||||||
|
if (logger.isErrorEnabled) {
|
||||||
|
logger.error(IllegalStateException("runCoroutineInPlace reached an unexpected state: coroutine did not finish."))
|
||||||
|
}
|
||||||
|
|
||||||
|
return runBlocking { lateResult.await() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val myStubFailure = Exception()
|
||||||
|
|
||||||
|
private class RunCoroutineInPlace
|
||||||
|
|
||||||
|
private val logger by lazy {
|
||||||
|
MiraiLogger.Factory.create(RunCoroutineInPlace::class)
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.CustomMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.FaceProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.FileMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.FlashImageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.IgnoredMessagesProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.ImageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.MarketFaceProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.MusicShareProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.PttMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.QuoteReplyProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.TextProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.UnsupportedMessageProtocol
|
||||||
|
net.mamoe.mirai.internal.message.protocol.impl.VipFaceProtocol
|
@ -12,6 +12,8 @@ package net.mamoe.mirai.internal.message
|
|||||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
|
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
|
||||||
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
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol.Companion.UNSUPPORTED_POKE_MESSAGE_PLAIN
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol.Companion.UNSUPPORTED_MERGED_MESSAGE_PLAIN
|
||||||
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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.internal.test.AbstractTest
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
internal class MessageProtocolFacadeTest : AbstractTest() {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can load`() {
|
||||||
|
assertEquals(
|
||||||
|
"""
|
||||||
|
QuoteReplyProtocol
|
||||||
|
CustomMessageProtocol
|
||||||
|
FileMessageProtocol
|
||||||
|
FlashImageProtocol
|
||||||
|
FaceProtocol
|
||||||
|
ImageProtocol
|
||||||
|
MarketFaceProtocol
|
||||||
|
MusicShareProtocol
|
||||||
|
PokeMessageProtocol
|
||||||
|
IgnoredMessagesProtocol
|
||||||
|
PttMessageProtocol
|
||||||
|
RichMessageProtocol
|
||||||
|
TextProtocol
|
||||||
|
UnsupportedMessageProtocol
|
||||||
|
VipFaceProtocol
|
||||||
|
""".trimIndent(),
|
||||||
|
MessageProtocolFacadeImpl().loaded.joinToString("\n") { it::class.simpleName.toString() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -9,16 +9,29 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.message.protocol.impl
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import net.mamoe.mirai.contact.ContactOrBot
|
||||||
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.internal.contact.inferMessageSourceKind
|
||||||
import net.mamoe.mirai.internal.message.protocol.*
|
import net.mamoe.mirai.internal.message.protocol.*
|
||||||
import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest
|
import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.internal.utils.structureToString
|
import net.mamoe.mirai.internal.notice.processors.GroupExtensions
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
|
import net.mamoe.mirai.message.data.MessageChainBuilder
|
||||||
|
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||||
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import kotlin.test.asserter
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest() {
|
internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions {
|
||||||
|
|
||||||
|
protected abstract val protocol: MessageProtocol
|
||||||
|
protected var defaultTarget: ContactOrBot? = null
|
||||||
|
|
||||||
private var decoderLoggerEnabled = false
|
private var decoderLoggerEnabled = false
|
||||||
private var encoderLoggerEnabled = false
|
private var encoderLoggerEnabled = false
|
||||||
@ -50,45 +63,39 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
protocol: MessageProtocol,
|
protocol: MessageProtocol,
|
||||||
encode: MessageProtocolFacade.() -> List<ImMsgBody.Elem>
|
encode: MessageProtocolFacade.() -> List<ImMsgBody.Elem>
|
||||||
) {
|
) {
|
||||||
assertEquals(
|
asserter.assertEquals(
|
||||||
expectedStruct,
|
expectedStruct,
|
||||||
facadeOf(protocol).encode(),
|
facadeOf(protocol).encode(),
|
||||||
message = "Failed to check single Protocol"
|
message = "Failed to check single Protocol"
|
||||||
)
|
)
|
||||||
assertEquals(
|
asserter.assertEquals(
|
||||||
expectedStruct,
|
expectedStruct,
|
||||||
MessageProtocolFacade.INSTANCE.encode(),
|
MessageProtocolFacade.INSTANCE.encode(),
|
||||||
message = "Failed to check with all protocols"
|
message = "Failed to check with all protocols"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
var asserter: EqualityAsserter = EqualityAsserter.OrdinaryThenStructural
|
||||||
private fun <@kotlin.internal.OnlyInputTypes T> assertEquals(
|
|
||||||
expected: List<T>,
|
fun useOrdinaryEquality() {
|
||||||
actual: List<T>,
|
asserter = EqualityAsserter.Ordinary
|
||||||
message: String? = null
|
}
|
||||||
) {
|
|
||||||
if (expected.size == 1 && actual.size == 1) {
|
fun useStructuralEquality() {
|
||||||
asserter.assertEquals(message, expected.single().structureToString(), actual.single().structureToString())
|
asserter = EqualityAsserter.Structural
|
||||||
} else {
|
|
||||||
asserter.assertEquals(
|
|
||||||
message,
|
|
||||||
expected.joinToString { it.structureToString() },
|
|
||||||
actual.joinToString { it.structureToString() })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun doDecoderChecks(
|
protected fun doDecoderChecks(
|
||||||
expectedChain: MessageChain,
|
expectedChain: MessageChain,
|
||||||
protocol: MessageProtocol,
|
protocol: MessageProtocol = this.protocol,
|
||||||
decode: MessageProtocolFacade.() -> MessageChain
|
decode: MessageProtocolFacade.() -> MessageChain
|
||||||
) {
|
) {
|
||||||
assertEquals(
|
asserter.assertEquals(
|
||||||
expectedChain.toList(),
|
expectedChain.toList(),
|
||||||
facadeOf(protocol).decode().toList(),
|
facadeOf(protocol).decode().toList(),
|
||||||
message = "Failed to check single Protocol"
|
message = "Failed to check single Protocol"
|
||||||
)
|
)
|
||||||
assertEquals(
|
asserter.assertEquals(
|
||||||
expectedChain.toList(),
|
expectedChain.toList(),
|
||||||
MessageProtocolFacade.INSTANCE.decode().toList(),
|
MessageProtocolFacade.INSTANCE.decode().toList(),
|
||||||
message = "Failed to check with all protocols"
|
message = "Failed to check with all protocols"
|
||||||
@ -96,8 +103,98 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun doEncoderChecks(
|
protected fun doEncoderChecks(
|
||||||
expectedStruct: ImMsgBody.Elem,
|
vararg expectedStruct: ImMsgBody.Elem,
|
||||||
protocol: MessageProtocol,
|
protocol: MessageProtocol = this.protocol,
|
||||||
encode: MessageProtocolFacade.() -> List<ImMsgBody.Elem>
|
encode: MessageProtocolFacade.() -> List<ImMsgBody.Elem>
|
||||||
): Unit = doEncoderChecks(mutableListOf(expectedStruct), protocol, encode)
|
): Unit = doEncoderChecks(expectedStruct.toList(), protocol, encode)
|
||||||
|
|
||||||
|
|
||||||
|
inner class ChecksBuilder {
|
||||||
|
var elems: MutableList<ImMsgBody.Elem> = mutableListOf()
|
||||||
|
var messages: MessageChainBuilder = MessageChainBuilder()
|
||||||
|
|
||||||
|
var groupIdOrZero: Long = 0
|
||||||
|
var messageSourceKind: MessageSourceKind = MessageSourceKind.GROUP
|
||||||
|
var target: ContactOrBot? = defaultTarget
|
||||||
|
var withGeneralFlags = true
|
||||||
|
var isForward = false
|
||||||
|
|
||||||
|
fun elem(vararg elem: ImMsgBody.Elem) {
|
||||||
|
elems.addAll(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun message(vararg message: SingleMessage) {
|
||||||
|
messages.addAll(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun target(target: ContactOrBot?) {
|
||||||
|
this.target = target
|
||||||
|
|
||||||
|
if (target != null) {
|
||||||
|
messageSourceKind = target.inferMessageSourceKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target is Group) {
|
||||||
|
groupIdOrZero = target.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forward() {
|
||||||
|
this.isForward = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build() = ChecksConfiguration(
|
||||||
|
elems.toList(),
|
||||||
|
messages.build(),
|
||||||
|
groupIdOrZero,
|
||||||
|
messageSourceKind,
|
||||||
|
target,
|
||||||
|
withGeneralFlags,
|
||||||
|
isForward
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChecksConfiguration(
|
||||||
|
val elems: List<ImMsgBody.Elem>,
|
||||||
|
val messageChain: MessageChain,
|
||||||
|
val groupIdOrZero: Long,
|
||||||
|
val messageSourceKind: MessageSourceKind,
|
||||||
|
val target: ContactOrBot?,
|
||||||
|
val withGeneralFlags: Boolean,
|
||||||
|
val isForward: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("DeferredIsResult")
|
||||||
|
protected fun buildChecks(
|
||||||
|
builderAction: ChecksBuilder.() -> Unit,
|
||||||
|
): Deferred<ChecksConfiguration> { // IDE will warn you if you forget to call .do
|
||||||
|
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||||
|
return CompletableDeferred(ChecksBuilder().apply(builderAction).build())
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
protected open fun Deferred<ChecksConfiguration>.doEncoderChecks() {
|
||||||
|
val config = this.getCompleted()
|
||||||
|
doEncoderChecks(config.elems, protocol) {
|
||||||
|
encode(
|
||||||
|
config.messageChain,
|
||||||
|
config.target,
|
||||||
|
withGeneralFlags = config.withGeneralFlags,
|
||||||
|
isForward = config.isForward
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
protected open fun Deferred<ChecksConfiguration>.doDecoderChecks() {
|
||||||
|
val config = this.getCompleted()
|
||||||
|
doDecoderChecks(config.messageChain, protocol) {
|
||||||
|
decode(config.elems, config.groupIdOrZero, config.messageSourceKind, bot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun Deferred<ChecksConfiguration>.doBothChecks() {
|
||||||
|
doEncoderChecks()
|
||||||
|
doDecoderChecks()
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.internal.message.protocol.impl
|
||||||
|
|
||||||
|
import net.mamoe.mirai.internal.utils.structureToString
|
||||||
|
import net.mamoe.mirai.internal.utils.structureToStringIfAvailable
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.asserter
|
||||||
|
|
||||||
|
internal interface EqualityAsserter {
|
||||||
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
fun <@kotlin.internal.OnlyInputTypes T> assertEquals(
|
||||||
|
expected: List<T>,
|
||||||
|
actual: List<T>,
|
||||||
|
message: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
object Ordinary : EqualityAsserter {
|
||||||
|
override fun <T> assertEquals(expected: List<T>, actual: List<T>, message: String?) {
|
||||||
|
if (expected.size == actual.size) {
|
||||||
|
if (expected.size == 1 && expected.singleOrNull() == actual.singleOrNull()) {
|
||||||
|
return asserter.assertEquals(message, expected.single(), actual.single())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected.zip(actual).all { (e, a) -> e == a }) return
|
||||||
|
|
||||||
|
asserter.assertEquals(message, expected, actual)
|
||||||
|
} else {
|
||||||
|
asserter.assertEquals(message, expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Structural : EqualityAsserter {
|
||||||
|
override fun <T> assertEquals(expected: List<T>, actual: List<T>, message: String?) {
|
||||||
|
if (expected.size == 1 && actual.size == 1) {
|
||||||
|
val e = expected.single()
|
||||||
|
val a = actual.single()
|
||||||
|
if (a == null || e == null) {
|
||||||
|
asserter.assertEquals(
|
||||||
|
"[Null] $message",
|
||||||
|
structureToStringOrOrdinaryString(e),
|
||||||
|
structureToStringOrOrdinaryString(a)
|
||||||
|
)
|
||||||
|
assertNotNull(a, message)
|
||||||
|
assertNotNull(e, message)
|
||||||
|
}
|
||||||
|
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||||
|
if (!e!!::class.isInstance(a) && !a!!::class.isInstance(e)) {
|
||||||
|
asserter.assertEquals(
|
||||||
|
"[Incompatible type] $message",
|
||||||
|
structureToStringOrOrdinaryString(e),
|
||||||
|
structureToStringOrOrdinaryString(a)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
asserter.assertEquals(
|
||||||
|
message,
|
||||||
|
structureToStringOrOrdinaryString(e),
|
||||||
|
structureToStringOrOrdinaryString(a)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
asserter.assertEquals(
|
||||||
|
message,
|
||||||
|
expected.joinToString { structureToStringOrOrdinaryString(it) },
|
||||||
|
actual.joinToString { structureToStringOrOrdinaryString(it) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> structureToStringOrOrdinaryString(it: T): String =
|
||||||
|
it.structureToString().ifBlank {
|
||||||
|
it.structureToStringIfAvailable() ?: error("structureToString is not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object OrdinaryThenStructural : EqualityAsserter {
|
||||||
|
override fun <T> assertEquals(expected: List<T>, actual: List<T>, message: String?) {
|
||||||
|
try {
|
||||||
|
Ordinary.assertEquals(expected, actual, message)
|
||||||
|
return
|
||||||
|
} catch (e: AssertionError) {
|
||||||
|
Structural.assertEquals(expected, actual, message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
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.message.data.Face
|
import net.mamoe.mirai.message.data.Face
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||||
import net.mamoe.mirai.message.data.messageChainOf
|
import net.mamoe.mirai.message.data.messageChainOf
|
||||||
@ -16,6 +17,7 @@ import net.mamoe.mirai.utils.hexToBytes
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
||||||
|
override val protocol: MessageProtocol = FaceProtocol()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can encode`() {
|
fun `can encode`() {
|
||||||
@ -27,7 +29,6 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
buf = "00 01 00 04 52 CC F5 D0".hexToBytes(),
|
buf = "00 01 00 04 52 CC F5 D0".hexToBytes(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FaceProtocol()
|
|
||||||
) {
|
) {
|
||||||
encode(
|
encode(
|
||||||
messageChainOf(Face(Face.PIE_ZUI)),
|
messageChainOf(Face(Face.PIE_ZUI)),
|
||||||
@ -40,7 +41,6 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
fun `can decode`() {
|
fun `can decode`() {
|
||||||
doDecoderChecks(
|
doDecoderChecks(
|
||||||
messageChainOf(Face(Face.YIN_XIAN)),
|
messageChainOf(Face(Face.YIN_XIAN)),
|
||||||
FaceProtocol()
|
|
||||||
) {
|
) {
|
||||||
decode(
|
decode(
|
||||||
listOf(
|
listOf(
|
||||||
@ -58,4 +58,5 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* 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.message.data.At
|
||||||
|
import net.mamoe.mirai.message.data.AtAll
|
||||||
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
|
import net.mamoe.mirai.utils.hexToBytes
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class TextProtocolTest : AbstractMessageProtocolTest() {
|
||||||
|
|
||||||
|
override val protocol = TextProtocol()
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun `init group`() {
|
||||||
|
defaultTarget = bot.addGroup(123, 1230003).apply {
|
||||||
|
addMember(1230003, "user3", MemberPermission.OWNER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test PlainText`() {
|
||||||
|
buildChecks {
|
||||||
|
elem(
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "hello",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
message(PlainText("hello"))
|
||||||
|
}.doBothChecks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test AtAll`() {
|
||||||
|
buildChecks {
|
||||||
|
elem(
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "@全体成员",
|
||||||
|
attr6Buf = "00 01 00 00 00 05 01 00 00 00 00 00 00".hexToBytes(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
message(AtAll)
|
||||||
|
}.doBothChecks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `AtAll auto append spaces`() {
|
||||||
|
buildChecks {
|
||||||
|
elem(
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "@全体成员",
|
||||||
|
attr6Buf = "00 01 00 00 00 05 01 00 00 00 00 00 00".hexToBytes(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "Hi",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
message(AtAll, PlainText("Hi"))
|
||||||
|
}.doEncoderChecks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test At`() {
|
||||||
|
buildChecks {
|
||||||
|
elem(
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "@user3",
|
||||||
|
attr6Buf = "00 01 00 00 00 06 00 00 12 C4 B3 00 00".hexToBytes(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
message(At(1230003))
|
||||||
|
}.doBothChecks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `At auto append spaces`() {
|
||||||
|
buildChecks {
|
||||||
|
elem(
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = "@user3",
|
||||||
|
attr6Buf = "00 01 00 00 00 06 00 00 12 C4 B3 00 00".hexToBytes(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
|
||||||
|
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
|
||||||
|
str = " ",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
message(At(1230003))
|
||||||
|
message(PlainText(" "))
|
||||||
|
target(bot.addGroup(123, 1230003).apply {
|
||||||
|
addMember(1230003, "user3", MemberPermission.OWNER)
|
||||||
|
})
|
||||||
|
}.doBothChecks()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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.testFramework.message.protocol
|
||||||
|
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoder
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoderContext
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
|
||||||
|
import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer.Companion.generateAndDesensitize
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import net.mamoe.mirai.utils.debug
|
||||||
|
|
||||||
|
internal class MessageDecodingRecorder(
|
||||||
|
private val logger: MiraiLogger = MiraiLogger.Factory.create(MessageDecodingRecorder::class)
|
||||||
|
) : MessageDecoder {
|
||||||
|
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||||
|
logger.debug {
|
||||||
|
"\n" + ValueDescAnalyzer.generateAndDesensitize(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.BotFactory
|
||||||
|
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageDecoderProcessor
|
||||||
|
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||||
|
import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
|
||||||
|
import net.mamoe.mirai.internal.testFramework.message.protocol.MessageDecodingRecorder
|
||||||
|
import net.mamoe.mirai.utils.BotConfiguration
|
||||||
|
import net.mamoe.mirai.utils.readResource
|
||||||
|
import net.mamoe.yamlkt.Yaml
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
suspend fun main() {
|
||||||
|
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
||||||
|
Bot.instances.forEach {
|
||||||
|
it.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Desensitizer.local.desensitize("") // verify rules
|
||||||
|
|
||||||
|
val account = Yaml.decodeFromString(LocalAccount.serializer(), readResource("local.account.yml"))
|
||||||
|
val bot = BotFactory.newBot(account.id, account.password) {
|
||||||
|
enableContactCache()
|
||||||
|
fileBasedDeviceInfo("local.device.json")
|
||||||
|
protocol = BotConfiguration.MiraiProtocol.ANDROID_PHONE
|
||||||
|
}.asQQAndroidBot()
|
||||||
|
|
||||||
|
MessageProtocolFacade.decoderPipeline.registerBefore(MessageDecoderProcessor(MessageDecodingRecorder()))
|
||||||
|
|
||||||
|
bot.login()
|
||||||
|
|
||||||
|
bot.join()
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 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.
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
Loading…
Reference in New Issue
Block a user