mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-17 09:09:23 +08:00
Redesign packet recording
This commit is contained in:
parent
c4939a7446
commit
76e2b6c64c
.run
mirai-core/src
commonMain/kotlin
QQAndroidBot.kt
contact
network
commonTest
jvmTest
21
.run/RunRecorderKt.run.xml
Normal file
21
.run/RunRecorderKt.run.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<!--
|
||||
~ Copyright 2019-2021 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="RunRecorderKt" type="JetRunConfigurationType" nameIsGenerated="true">
|
||||
<option name="MAIN_CLASS_NAME" value="net.mamoe.mirai.internal.bootstrap.RunRecorderKt"/>
|
||||
<module name="mirai.mirai-core.jvmTest"/>
|
||||
<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"/>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true"/>
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -42,6 +42,7 @@ import net.mamoe.mirai.internal.network.notice.UnconsumedNoticesAlerter
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.GroupNotificationDecoder
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.MsgInfoDecoder
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupNotificationProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupOrMemberListNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupRecallProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.priv.FriendNoticeProcessor
|
||||
@ -170,6 +171,7 @@ internal open class QQAndroidBot constructor(
|
||||
FriendNoticeProcessor(pipelineLogger.subLogger("FriendNoticeProcessor")),
|
||||
GroupOrMemberListNoticeProcessor(pipelineLogger.subLogger("GroupOrMemberListNoticeProcessor")),
|
||||
GroupMessageProcessor(pipelineLogger.subLogger("GroupMessageProcessor")),
|
||||
GroupNotificationProcessor(pipelineLogger.subLogger("GroupNotificationProcessor")),
|
||||
PrivateMessageProcessor(),
|
||||
OtherClientNoticeProcessor(),
|
||||
GroupRecallProcessor(),
|
||||
@ -206,9 +208,9 @@ internal open class QQAndroidBot constructor(
|
||||
set(
|
||||
PacketHandler,
|
||||
PacketHandlerChain(
|
||||
LoggingPacketHandlerAdapter(get(PacketLoggingStrategy), networkLogger),
|
||||
EventBroadcasterPacketHandler(components),
|
||||
CallPacketFactoryPacketHandler(bot),
|
||||
LoggingPacketHandlerAdapter(get(PacketLoggingStrategy), networkLogger),
|
||||
),
|
||||
)
|
||||
set(PacketCodec, PacketCodecImpl())
|
||||
|
@ -65,13 +65,37 @@ internal fun Group.checkIsGroupImpl(): GroupImpl {
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun GroupImpl(
|
||||
bot: QQAndroidBot,
|
||||
parentCoroutineContext: CoroutineContext,
|
||||
id: Long,
|
||||
groupInfo: GroupInfo,
|
||||
members: Sequence<MemberInfo>,
|
||||
): GroupImpl {
|
||||
return GroupImpl(bot, parentCoroutineContext, id, groupInfo, ContactList(ConcurrentLinkedQueue())).apply Group@{
|
||||
members.forEach { info ->
|
||||
if (info.uin == bot.id) {
|
||||
botAsMember = newNormalMember(info)
|
||||
if (info.permission == MemberPermission.OWNER) {
|
||||
owner = botAsMember
|
||||
}
|
||||
} else newNormalMember(info).let { member ->
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
owner = member
|
||||
}
|
||||
this@Group.members.delegate.add(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal class GroupImpl(
|
||||
internal class GroupImpl constructor(
|
||||
bot: QQAndroidBot,
|
||||
parentCoroutineContext: CoroutineContext,
|
||||
override val id: Long,
|
||||
groupInfo: GroupInfo,
|
||||
members: Sequence<MemberInfo>,
|
||||
override val members: ContactList<NormalMemberImpl>,
|
||||
) : Group, AbstractContact(bot, parentCoroutineContext) {
|
||||
companion object
|
||||
|
||||
@ -84,20 +108,6 @@ internal class GroupImpl(
|
||||
|
||||
override val filesRoot: RemoteFile by lazy { RemoteFileImpl(this, "/") }
|
||||
|
||||
override val members: ContactList<NormalMemberImpl> =
|
||||
ContactList(members.mapNotNullTo(ConcurrentLinkedQueue()) { info ->
|
||||
if (info.uin == bot.id) {
|
||||
botAsMember = newNormalMember(info)
|
||||
if (info.permission == MemberPermission.OWNER) {
|
||||
owner = botAsMember
|
||||
}
|
||||
null
|
||||
} else newNormalMember(info).also { member ->
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
owner = member
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
override val announcements: Announcements by lazy {
|
||||
AnnouncementsImpl(
|
||||
|
@ -30,6 +30,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.OnlinePushP
|
||||
import net.mamoe.mirai.internal.network.toPacket
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.io.Closeable
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import kotlin.reflect.KClass
|
||||
@ -40,7 +41,15 @@ internal typealias ProcessResult = Collection<Packet>
|
||||
* Centralized processor pipeline for [MessageSvcPbGetMsg] and [OnlinePushPbPushTransMsg]
|
||||
*/
|
||||
internal interface NoticeProcessorPipeline {
|
||||
fun registerProcessor(processor: NoticeProcessor)
|
||||
fun interface DisposableRegistry : Closeable {
|
||||
fun dispose()
|
||||
|
||||
override fun close() {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fun registerProcessor(processor: NoticeProcessor): DisposableRegistry
|
||||
|
||||
/**
|
||||
* Process [data] into [Packet]s. Exceptions are wrapped into [ParseErrorPacket]
|
||||
@ -69,7 +78,7 @@ internal value class MutableProcessResult(
|
||||
val data: MutableCollection<Packet>
|
||||
)
|
||||
|
||||
internal interface PipelineContext : BotAware {
|
||||
internal interface NoticePipelineContext : BotAware {
|
||||
override val bot: QQAndroidBot
|
||||
|
||||
val attributes: TypeSafeMap
|
||||
@ -128,28 +137,32 @@ internal interface PipelineContext : BotAware {
|
||||
val KEY_FROM_SYNC = TypeKey<Boolean>("fromSync")
|
||||
val KEY_MSG_INFO = TypeKey<MsgInfo>("msgInfo")
|
||||
|
||||
val PipelineContext.fromSync get() = attributes[KEY_FROM_SYNC]
|
||||
val NoticePipelineContext.fromSync get() = attributes[KEY_FROM_SYNC]
|
||||
|
||||
/**
|
||||
* 来自 [MsgInfo] 的数据, 即 [MsgType0x210], [MsgType0x2DC] 的处理过程之中可以使用
|
||||
*/
|
||||
val PipelineContext.msgInfo get() = attributes[KEY_MSG_INFO]
|
||||
val NoticePipelineContext.msgInfo get() = attributes[KEY_MSG_INFO]
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class AbstractPipelineContext(
|
||||
internal abstract class AbstractNoticePipelineContext(
|
||||
override val bot: QQAndroidBot, override val attributes: TypeSafeMap,
|
||||
) : PipelineContext {
|
||||
) : NoticePipelineContext {
|
||||
private val consumers: Stack<Any> = Stack()
|
||||
|
||||
override val isConsumed: Boolean get() = consumers.isNotEmpty()
|
||||
override fun NoticeProcessor.markAsConsumed(marker: Any) {
|
||||
traceLogging.info { "markAsConsumed: marker=$marker" }
|
||||
consumers.push(marker)
|
||||
}
|
||||
|
||||
override fun NoticeProcessor.markNotConsumed(marker: Any) {
|
||||
if (consumers.peek() === marker) {
|
||||
consumers.pop()
|
||||
traceLogging.info { "markNotConsumed: Y, marker=$marker" }
|
||||
} else {
|
||||
traceLogging.info { "markNotConsumed: N, marker=$marker" }
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,17 +170,27 @@ internal abstract class AbstractPipelineContext(
|
||||
|
||||
override fun collect(packet: Packet) {
|
||||
collected.data.add(packet)
|
||||
traceLogging.info { "collect: $packet" }
|
||||
}
|
||||
|
||||
override fun collect(packets: Iterable<Packet>) {
|
||||
this.collected.data.addAll(packets)
|
||||
traceLogging.info {
|
||||
val list = packets.toList()
|
||||
"collect: [${list.size}] ${list.joinToString()}"
|
||||
}
|
||||
}
|
||||
|
||||
abstract override suspend fun processAlso(data: ProtocolStruct, attributes: TypeSafeMap): ProcessResult
|
||||
}
|
||||
|
||||
|
||||
internal inline val PipelineContext.context get() = this
|
||||
internal inline val NoticePipelineContext.context get() = this
|
||||
|
||||
private val traceLogging: MiraiLogger by lazy {
|
||||
MiraiLogger.Factory.create(NoticeProcessorPipelineImpl::class, "NoticeProcessorPipeline")
|
||||
.withSwitch(systemProp("mirai.network.notice.pipeline.log.full", false))
|
||||
}
|
||||
|
||||
internal open class NoticeProcessorPipelineImpl private constructor() : NoticeProcessorPipeline {
|
||||
/**
|
||||
@ -175,24 +198,37 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
|
||||
*/
|
||||
private val processors = ConcurrentLinkedQueue<NoticeProcessor>()
|
||||
|
||||
override fun registerProcessor(processor: NoticeProcessor) {
|
||||
override fun registerProcessor(processor: NoticeProcessor): NoticeProcessorPipeline.DisposableRegistry {
|
||||
processors.add(processor)
|
||||
return NoticeProcessorPipeline.DisposableRegistry {
|
||||
processors.remove(processor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inner class ContextImpl(
|
||||
bot: QQAndroidBot, attributes: TypeSafeMap,
|
||||
) : AbstractPipelineContext(bot, attributes) {
|
||||
) : AbstractNoticePipelineContext(bot, attributes) {
|
||||
override suspend fun processAlso(data: ProtocolStruct, attributes: TypeSafeMap): ProcessResult {
|
||||
return process(bot, data, this.attributes + attributes)
|
||||
traceLogging.info { "processAlso: data=$data" }
|
||||
return process(bot, data, this.attributes + attributes).also {
|
||||
this.collected.data += it
|
||||
traceLogging.info { "processAlso: result=$it" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun process(bot: QQAndroidBot, data: ProtocolStruct, attributes: TypeSafeMap): ProcessResult {
|
||||
traceLogging.info { "process: data=$data" }
|
||||
val context = ContextImpl(bot, attributes)
|
||||
|
||||
val diff = if (traceLogging.isEnabled) CollectionDiff<Packet>() else null
|
||||
diff?.save(context.collected.data)
|
||||
|
||||
for (processor in processors) {
|
||||
kotlin.runCatching {
|
||||
|
||||
val result = kotlin.runCatching {
|
||||
processor.process(context, data)
|
||||
}.onFailure { e ->
|
||||
context.collect(
|
||||
@ -205,6 +241,16 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
diff?.run {
|
||||
val diffPackets = subtractAndSave(context.collected.data)
|
||||
|
||||
traceLogging.info {
|
||||
"Finished ${
|
||||
processor.toString().replace("net.mamoe.mirai.internal.network.notice.", "")
|
||||
}, success=${result.isSuccess}, consumed=${context.isConsumed}, diff=$diffPackets"
|
||||
}
|
||||
}
|
||||
}
|
||||
return context.collected.data
|
||||
}
|
||||
@ -231,7 +277,7 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
|
||||
* A processor handling some specific type of message.
|
||||
*/
|
||||
internal interface NoticeProcessor {
|
||||
suspend fun process(context: PipelineContext, data: Any?)
|
||||
suspend fun process(context: NoticePipelineContext, data: Any?)
|
||||
}
|
||||
|
||||
internal abstract class AnyNoticeProcessor : SimpleNoticeProcessor<ProtocolStruct>(type())
|
||||
@ -240,13 +286,13 @@ internal abstract class SimpleNoticeProcessor<in T : ProtocolStruct>(
|
||||
private val type: KClass<T>,
|
||||
) : NoticeProcessor {
|
||||
|
||||
final override suspend fun process(context: PipelineContext, data: Any?) {
|
||||
final override suspend fun process(context: NoticePipelineContext, data: Any?) {
|
||||
if (type.isInstance(data)) {
|
||||
context.processImpl(data.uncheckedCast())
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract suspend fun PipelineContext.processImpl(data: T)
|
||||
protected abstract suspend fun NoticePipelineContext.processImpl(data: T)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@ -255,11 +301,11 @@ internal abstract class SimpleNoticeProcessor<in T : ProtocolStruct>(
|
||||
}
|
||||
|
||||
internal abstract class MsgCommonMsgProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
|
||||
abstract override suspend fun PipelineContext.processImpl(data: MsgComm.Msg)
|
||||
abstract override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg)
|
||||
}
|
||||
|
||||
internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
|
||||
final override suspend fun PipelineContext.processImpl(data: ProtocolStruct) {
|
||||
final override suspend fun NoticePipelineContext.processImpl(data: ProtocolStruct) {
|
||||
when (data) {
|
||||
is PbMsgInfo -> processImpl(data)
|
||||
is MsgOnlinePush.PbPushMsg -> processImpl(data)
|
||||
@ -272,13 +318,13 @@ internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
|
||||
}
|
||||
}
|
||||
|
||||
protected open suspend fun PipelineContext.processImpl(data: MsgType0x210) {} // 528
|
||||
protected open suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {} // 732
|
||||
protected open suspend fun PipelineContext.processImpl(data: PbMsgInfo) {}
|
||||
protected open suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {}
|
||||
protected open suspend fun PipelineContext.processImpl(data: MsgComm.Msg) {}
|
||||
protected open suspend fun PipelineContext.processImpl(data: Structmsg.StructMsg) {}
|
||||
protected open suspend fun PipelineContext.processImpl(data: RequestPushStatus) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) {} // 528
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: MsgType0x2DC) {} // 732
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: PbMsgInfo) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: Structmsg.StructMsg) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: RequestPushStatus) {}
|
||||
|
||||
protected open suspend fun PipelineContext.processImpl(data: DecodedNotifyMsgBody) {}
|
||||
protected open suspend fun NoticePipelineContext.processImpl(data: DecodedNotifyMsgBody) {}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.internal.network.notice
|
||||
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
@ -22,11 +22,11 @@ internal class TraceLoggingNoticeProcessor(
|
||||
) : SimpleNoticeProcessor<ProtocolStruct>(type()) {
|
||||
private val logger: MiraiLogger = logger.withSwitch(systemProp("mirai.network.notice.trace.logging", false))
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: ProtocolStruct) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: ProtocolStruct) {
|
||||
logger.warning { "${data::class.simpleName}: isConsumed=$isConsumed" }
|
||||
}
|
||||
|
||||
// override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
|
||||
// override suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) {
|
||||
// logger.warning { "MsgType0x210: isConsumed=$isConsumed" }
|
||||
// }
|
||||
//
|
||||
|
@ -11,7 +11,7 @@ package net.mamoe.mirai.internal.network.notice
|
||||
|
||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.MsgType0x2DC
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
|
||||
@ -28,7 +28,7 @@ internal class UnconsumedNoticesAlerter(
|
||||
) : MixedNoticeProcessor() {
|
||||
private val logger: MiraiLogger = logger.withSwitch(systemProp("mirai.network.notice.unconsumed.logging", false))
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) {
|
||||
if (isConsumed) return
|
||||
when (data.uSubMsgType) {
|
||||
0x26L, // VIP 进群提示
|
||||
@ -50,12 +50,12 @@ internal class UnconsumedNoticesAlerter(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
if (isConsumed) return
|
||||
logger.debug { "Unknown group 732 type ${data.kind}, data: " + data.buf.toUHexString() }
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: OnlinePushTrans.PbMsgInfo) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: OnlinePushTrans.PbMsgInfo) {
|
||||
if (isConsumed) return
|
||||
when {
|
||||
data.msgType == 529 && data.msgSubtype == 9 -> {
|
||||
@ -89,12 +89,12 @@ internal class UnconsumedNoticesAlerter(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
|
||||
if (isConsumed) return
|
||||
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) {
|
||||
if (isConsumed) return
|
||||
when (data.msgHead.msgType) {
|
||||
732 -> {
|
||||
@ -121,7 +121,7 @@ internal class UnconsumedNoticesAlerter(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: Structmsg.StructMsg) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: Structmsg.StructMsg) {
|
||||
if (isConsumed) return
|
||||
if (logger.isEnabled && logger.isDebugEnabled) {
|
||||
data.msg?.context {
|
||||
@ -134,7 +134,7 @@ internal class UnconsumedNoticesAlerter(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: RequestPushStatus) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: RequestPushStatus) {
|
||||
if (isConsumed) return
|
||||
if (logger.isEnabled && logger.isDebugEnabled) {
|
||||
throw contextualBugReportException(
|
||||
|
@ -11,13 +11,13 @@ package net.mamoe.mirai.internal.network.notice.decoders
|
||||
|
||||
import net.mamoe.mirai.internal.contact.GroupImpl
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
|
||||
internal class GroupNotificationDecoder : MixedNoticeProcessor() {
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
when (data.kind) {
|
||||
0x10 -> {
|
||||
val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
|
||||
|
@ -14,8 +14,9 @@ import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.readUInt
|
||||
import net.mamoe.mirai.internal.contact.GroupImpl
|
||||
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext.Companion.KEY_MSG_INFO
|
||||
import net.mamoe.mirai.internal.getGroupByUin
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_MSG_INFO
|
||||
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.SyncController.Companion.syncController
|
||||
import net.mamoe.mirai.internal.network.components.syncOnlinePush
|
||||
@ -36,7 +37,7 @@ import net.mamoe.mirai.utils.toUHexString
|
||||
internal class MsgInfoDecoder(
|
||||
private val logger: MiraiLogger,
|
||||
) : SimpleNoticeProcessor<SvcReqPushMsg>(type()) {
|
||||
override suspend fun PipelineContext.processImpl(data: SvcReqPushMsg) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: SvcReqPushMsg) {
|
||||
// SvcReqPushMsg is fully handled here, no need to set consumed.
|
||||
|
||||
for (msgInfo in data.vMsgInfos) {
|
||||
@ -44,16 +45,20 @@ internal class MsgInfoDecoder(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun PipelineContext.decodeMsgInfo(data: MsgInfo) {
|
||||
private suspend fun NoticePipelineContext.decodeMsgInfo(data: MsgInfo) {
|
||||
if (!bot.syncController.syncOnlinePush(data)) return
|
||||
when (data.shMsgType.toUShort().toInt()) {
|
||||
@Suppress("MoveVariableDeclarationIntoWhen") // for debug
|
||||
val id = data.shMsgType.toUShort().toInt()
|
||||
when (id) {
|
||||
// 528
|
||||
0x210 -> processAlso(data.vMsg.loadAs(MsgType0x210.serializer()), KEY_MSG_INFO to data)
|
||||
|
||||
// 732
|
||||
0x2dc -> {
|
||||
data.vMsg.read {
|
||||
val group = bot.getGroup(readUInt().toLong()) ?: return // group has not been initialized
|
||||
val groupCode = readUInt().toLong()
|
||||
val group = bot.getGroup(groupCode) ?: bot.getGroupByUin(groupCode)
|
||||
?: return // group has not been initialized
|
||||
group.checkIsGroupImpl()
|
||||
|
||||
val kind = readByte().toInt()
|
||||
|
@ -23,7 +23,7 @@ import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
||||
import net.mamoe.mirai.internal.contact.newAnonymous
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.SyncController.Companion.syncController
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.MemberNick.Companion.generateMemberNickFromMember
|
||||
@ -67,7 +67,7 @@ internal class GroupMessageProcessor(
|
||||
}
|
||||
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
|
||||
val msgHead = data.msg.msgHead
|
||||
|
||||
val isFromSelfAccount = msgHead.fromUin == bot.id
|
||||
|
@ -20,8 +20,7 @@ import net.mamoe.mirai.internal.contact.checkIsGroupImpl
|
||||
import net.mamoe.mirai.internal.contact.checkIsMemberImpl
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.handler.logger
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.notice.NewContactSupport
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.MsgType0x2DC
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
|
||||
@ -32,9 +31,11 @@ import net.mamoe.mirai.internal.utils._miraiContentToString
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.utils.*
|
||||
|
||||
internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSupport {
|
||||
internal class GroupNotificationProcessor(
|
||||
private val logger: MiraiLogger,
|
||||
) : MixedNoticeProcessor(), NewContactSupport {
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x210) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) = data.context {
|
||||
when (data.uSubMsgType) {
|
||||
0x27L -> {
|
||||
val body = vProtobuf.loadAs(Submsgtype0x27.SubMsgType0x27.SubMsgType0x27MsgBody.serializer())
|
||||
@ -53,7 +54,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
/**
|
||||
* @see GroupNameChangeEvent
|
||||
*/
|
||||
private fun PipelineContext.handleGroupProfileChanged(
|
||||
private fun NoticePipelineContext.handleGroupProfileChanged(
|
||||
modGroupProfile: Submsgtype0x27.SubMsgType0x27.ModGroupProfile
|
||||
) {
|
||||
for (info in modGroupProfile.msgGroupProfileInfos) {
|
||||
@ -111,7 +112,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
/**
|
||||
* @see MemberCardChangeEvent
|
||||
*/
|
||||
private fun PipelineContext.handleGroupMemberProfileChanged(
|
||||
private fun NoticePipelineContext.handleGroupMemberProfileChanged(
|
||||
modGroupMemberProfile: Submsgtype0x27.SubMsgType0x27.ModGroupMemberProfile
|
||||
) {
|
||||
for (info in modGroupMemberProfile.msgGroupMemberProfileInfos) {
|
||||
@ -132,14 +133,14 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
}
|
||||
2 -> {
|
||||
if (info.value.singleOrNull()?.code != 0) {
|
||||
bot.logger.debug {
|
||||
logger.debug {
|
||||
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
else -> {
|
||||
bot.logger.debug {
|
||||
logger.debug {
|
||||
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
|
||||
}
|
||||
continue
|
||||
@ -153,7 +154,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
// MsgType0x2DC
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
when (data.kind) {
|
||||
0x0C -> processMute(data)
|
||||
0x0E -> processAllowAnonymousChat(data)
|
||||
@ -169,7 +170,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
* @see BotMuteEvent
|
||||
* @see BotUnmuteEvent
|
||||
*/
|
||||
private fun PipelineContext.processMute(
|
||||
private fun NoticePipelineContext.processMute(
|
||||
data: MsgType0x2DC,
|
||||
) = data.context {
|
||||
fun handleMuteMemberPacket(
|
||||
@ -232,7 +233,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
/**
|
||||
* @see GroupAllowAnonymousChatEvent
|
||||
*/
|
||||
private fun PipelineContext.processAllowAnonymousChat(
|
||||
private fun NoticePipelineContext.processAllowAnonymousChat(
|
||||
data: MsgType0x2DC,
|
||||
) = data.context {
|
||||
markAsConsumed()
|
||||
@ -249,7 +250,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
/**
|
||||
* @see GroupAllowConfessTalkEvent
|
||||
*/
|
||||
private fun PipelineContext.processAllowConfessTask(
|
||||
private fun NoticePipelineContext.processAllowConfessTask(
|
||||
data: MsgType0x2DC,
|
||||
) = data.context {
|
||||
val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
|
||||
@ -268,7 +269,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
"管理员已关闭群聊坦白说" -> false
|
||||
"管理员已开启群聊坦白说" -> true
|
||||
else -> {
|
||||
bot.network.logger.debug { "Unknown server confess talk messages $message" }
|
||||
logger.debug { "Unknown server confess talk messages $message" }
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -286,7 +287,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
* @see MemberHonorChangeEvent
|
||||
* @see GroupTalkativeChangeEvent
|
||||
*/ // gray tip: 聊天中的灰色小框系统提示信息
|
||||
private fun PipelineContext.processGrayTip(
|
||||
private fun NoticePipelineContext.processGrayTip(
|
||||
data: MsgType0x2DC,
|
||||
) = data.context {
|
||||
val grayTip = buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), 1).optGeneralGrayTip
|
||||
@ -324,7 +325,7 @@ internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSu
|
||||
}
|
||||
else -> {
|
||||
markNotConsumed()
|
||||
bot.network.logger.debug {
|
||||
logger.debug {
|
||||
"Unknown Transformers528 0x14 template\ntemplId=${grayTip?.templId}\nPermList=${grayTip?.msgTemplParam?._miraiContentToString()}"
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import net.mamoe.mirai.internal.getGroupByUin
|
||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||
import net.mamoe.mirai.internal.network.components.ContactUpdater
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.notice.NewContactSupport
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.DecodedNotifyMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
|
||||
@ -61,7 +61,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
private val logger: MiraiLogger,
|
||||
) : MixedNoticeProcessor(), NewContactSupport {
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) {
|
||||
if (data.uSubMsgType != 0x44L) return
|
||||
markAsConsumed()
|
||||
val msg = data.vProtobuf.loadAs(Submsgtype0x44.Submsgtype0x44.MsgBody.serializer())
|
||||
@ -82,7 +82,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
* @see MemberJoinEvent.Invite
|
||||
* @see MemberLeaveEvent.Quit
|
||||
*/
|
||||
override suspend fun PipelineContext.processImpl(data: DecodedNotifyMsgBody) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: DecodedNotifyMsgBody) = data.context {
|
||||
val proto = data.buf
|
||||
if (proto.optEnumType != 1) return
|
||||
val tipsInfo = proto.optMsgGraytips ?: return
|
||||
@ -120,7 +120,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
* @see MemberJoinEvent.Active
|
||||
* @see BotJoinGroupEvent.Active
|
||||
*/
|
||||
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
bot.components[ContactUpdater].groupListModifyLock.withLock {
|
||||
when (data.msgHead.msgType) {
|
||||
33 -> processGroupJoin33(data)
|
||||
@ -134,7 +134,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
}
|
||||
|
||||
// 33
|
||||
private suspend fun PipelineContext.processGroupJoin33(data: MsgComm.Msg) = data.context {
|
||||
private suspend fun NoticePipelineContext.processGroupJoin33(data: MsgComm.Msg) = data.context {
|
||||
msgBody.msgContent.read {
|
||||
val groupUin = Mirai.calculateGroupUinByGroupCode(readUInt().toLong())
|
||||
val group = bot.getGroupByUin(groupUin) ?: bot.addNewGroupByUin(groupUin) ?: return
|
||||
@ -178,13 +178,13 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
}
|
||||
|
||||
// 38
|
||||
private suspend fun PipelineContext.processGroupJoin38(data: MsgComm.Msg) = data.context {
|
||||
private suspend fun NoticePipelineContext.processGroupJoin38(data: MsgComm.Msg) = data.context {
|
||||
if (bot.getGroupByUin(msgHead.fromUin) != null) return
|
||||
bot.addNewGroupByUin(msgHead.fromUin)?.let { collect(BotJoinGroupEvent.Active(it)) }
|
||||
}
|
||||
|
||||
// 85
|
||||
private suspend fun PipelineContext.processGroupJoin85(data: MsgComm.Msg) = data.context {
|
||||
private suspend fun NoticePipelineContext.processGroupJoin85(data: MsgComm.Msg) = data.context {
|
||||
// msgHead.authUin: 处理人
|
||||
if (msgHead.toUin != bot.id) return
|
||||
processGroupJoin38(data)
|
||||
@ -194,7 +194,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
// Structmsg.StructMsg
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: Structmsg.StructMsg) = data.msg.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: Structmsg.StructMsg) = data.msg.context {
|
||||
if (this == null) return
|
||||
markAsConsumed()
|
||||
when (subType) {
|
||||
@ -270,7 +270,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
// OnlinePushTrans.PbMsgInfo
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: OnlinePushTrans.PbMsgInfo) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: OnlinePushTrans.PbMsgInfo) {
|
||||
markAsConsumed()
|
||||
when (data.msgType) {
|
||||
44 -> data.msgData.read {
|
||||
@ -327,7 +327,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleLeave(
|
||||
private fun NoticePipelineContext.handleLeave(
|
||||
target: Long,
|
||||
kind: Int,
|
||||
operator: Long,
|
||||
@ -368,7 +368,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
* @see BotGroupPermissionChangeEvent
|
||||
* @see MemberPermissionChangeEvent
|
||||
*/
|
||||
private fun PipelineContext.handlePermissionChange(
|
||||
private fun NoticePipelineContext.handlePermissionChange(
|
||||
data: OnlinePushTrans.PbMsgInfo,
|
||||
target: Long,
|
||||
newPermissionByte: Int,
|
||||
@ -395,7 +395,7 @@ internal class GroupOrMemberListNoticeProcessor(
|
||||
* Owner of the group [from] transfers ownership to another member [to], or retrieve ownership.
|
||||
*/
|
||||
// TODO: 2021/6/26 tests
|
||||
private suspend fun PipelineContext.handleGroupOwnershipTransfer(
|
||||
private suspend fun NoticePipelineContext.handleGroupOwnershipTransfer(
|
||||
data: OnlinePushTrans.PbMsgInfo,
|
||||
from: Long,
|
||||
to: Long,
|
||||
|
@ -11,15 +11,16 @@ package net.mamoe.mirai.internal.network.notice.group
|
||||
|
||||
import net.mamoe.mirai.event.events.MessageRecallEvent
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.notice.decoders.MsgType0x2DC
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.utils.mapToIntArray
|
||||
|
||||
internal class GroupRecallProcessor : MixedNoticeProcessor() {
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
val (_, group, buf) = data
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x2DC) {
|
||||
val (kind, group, buf) = data
|
||||
if (kind != 0x11) return
|
||||
|
||||
val proto = buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), 1)
|
||||
|
||||
|
@ -21,8 +21,8 @@ import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
|
||||
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
|
||||
import net.mamoe.mirai.internal.contact.toMiraiFriendInfo
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext.Companion.msgInfo
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.msgInfo
|
||||
import net.mamoe.mirai.internal.network.notice.NewContactSupport
|
||||
import net.mamoe.mirai.internal.network.notice.group.get
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
|
||||
@ -50,7 +50,7 @@ import net.mamoe.mirai.utils.*
|
||||
internal class FriendNoticeProcessor(
|
||||
private val logger: MiraiLogger,
|
||||
) : MixedNoticeProcessor(), NewContactSupport {
|
||||
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
if (msgHead.msgType != 191) return
|
||||
|
||||
var fromGroup = 0L
|
||||
@ -93,7 +93,7 @@ internal class FriendNoticeProcessor(
|
||||
|
||||
}
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: MsgType0x210) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgType0x210) = data.context {
|
||||
markAsConsumed()
|
||||
when (data.uSubMsgType) {
|
||||
0xB3L -> {
|
||||
@ -166,7 +166,7 @@ internal class FriendNoticeProcessor(
|
||||
@ProtoNumber(5) val reserved: ByteArray? = null, // struct{ boolean(1), boolean(2) }
|
||||
) : ProtoBuf
|
||||
|
||||
private fun PipelineContext.processFriendRecall(body: Sub8A) {
|
||||
private fun NoticePipelineContext.processFriendRecall(body: Sub8A) {
|
||||
for (info in body.msgInfo) {
|
||||
if (info.botUin != bot.id) continue
|
||||
collected += MessageRecallEvent.FriendRecall(
|
||||
@ -181,13 +181,13 @@ internal class FriendNoticeProcessor(
|
||||
}
|
||||
|
||||
|
||||
private fun PipelineContext.handleInputStatusChanged(body: SubMsgType0x115.MsgBody) {
|
||||
private fun NoticePipelineContext.handleInputStatusChanged(body: SubMsgType0x115.MsgBody) {
|
||||
val friend = bot.getFriend(body.fromUin) ?: return
|
||||
val item = body.msgNotifyItem ?: return
|
||||
collect(FriendInputStatusChangedEvent(friend, item.eventType == 1))
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleProfileChanged(body: ModProfile) {
|
||||
private fun NoticePipelineContext.handleProfileChanged(body: ModProfile) {
|
||||
var containsUnknown = false
|
||||
for (profileInfo in body.msgProfileInfos) {
|
||||
when (profileInfo.field) {
|
||||
@ -214,7 +214,7 @@ internal class FriendNoticeProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleRemarkChanged(body: ModFriendRemark) {
|
||||
private fun NoticePipelineContext.handleRemarkChanged(body: ModFriendRemark) {
|
||||
for (new in body.msgFrdRmk) {
|
||||
val friend = bot.getFriend(new.fuin)?.impl() ?: continue
|
||||
|
||||
@ -223,20 +223,20 @@ internal class FriendNoticeProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleAvatarChanged(body: ModCustomFace) {
|
||||
private fun NoticePipelineContext.handleAvatarChanged(body: ModCustomFace) {
|
||||
if (body.uin == bot.id) {
|
||||
collect(BotAvatarChangedEvent(bot))
|
||||
}
|
||||
collect(FriendAvatarChangedEvent(bot.getFriend(body.uin) ?: return))
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleFriendDeleted(body: DelFriend) {
|
||||
private fun NoticePipelineContext.handleFriendDeleted(body: DelFriend) {
|
||||
for (id in body.uint64Uins) {
|
||||
collect(FriendDeleteEvent(bot.removeFriend(id) ?: continue))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun PipelineContext.handleFriendAddedA(
|
||||
private suspend fun NoticePipelineContext.handleFriendAddedA(
|
||||
body: Submsgtype0x44.MsgBody,
|
||||
) = body.msgFriendMsgSync.context {
|
||||
if (this == null) return
|
||||
@ -255,20 +255,21 @@ internal class FriendNoticeProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun PipelineContext.handleFriendAddedB(data: MsgType0x210, body: SubMsgType0xb3.MsgBody) = data.context {
|
||||
val info = FriendInfoImpl(
|
||||
uin = body.msgAddFrdNotify.fuin,
|
||||
nick = body.msgAddFrdNotify.fuinNick,
|
||||
remark = "",
|
||||
)
|
||||
private fun NoticePipelineContext.handleFriendAddedB(data: MsgType0x210, body: SubMsgType0xb3.MsgBody) =
|
||||
data.context {
|
||||
val info = FriendInfoImpl(
|
||||
uin = body.msgAddFrdNotify.fuin,
|
||||
nick = body.msgAddFrdNotify.fuinNick,
|
||||
remark = "",
|
||||
)
|
||||
|
||||
val removed = bot.removeStranger(info.uin)
|
||||
val added = bot.addNewFriendAndRemoveStranger(info) ?: return
|
||||
collect(FriendAddEvent(added))
|
||||
if (removed != null) collect(StrangerRelationChangeEvent.Friended(removed, added))
|
||||
val removed = bot.removeStranger(info.uin)
|
||||
val added = bot.addNewFriendAndRemoveStranger(info) ?: return
|
||||
collect(FriendAddEvent(added))
|
||||
if (removed != null) collect(StrangerRelationChangeEvent.Friended(removed, added))
|
||||
}
|
||||
|
||||
private fun PipelineContext.handlePrivateNudge(body: Submsgtype0x122.Submsgtype0x122.MsgBody) {
|
||||
private fun NoticePipelineContext.handlePrivateNudge(body: Submsgtype0x122.Submsgtype0x122.MsgBody) {
|
||||
val action = body.msgTemplParam["action_str"].orEmpty()
|
||||
val from = body.msgTemplParam["uin_str1"]?.findFriendOrStranger() ?: bot.asFriend
|
||||
val target = body.msgTemplParam["uin_str2"]?.findFriendOrStranger() ?: bot.asFriend
|
||||
|
@ -26,7 +26,7 @@ import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
|
||||
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||
import net.mamoe.mirai.internal.network.components.ContactUpdater
|
||||
import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.handler.logger
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
@ -48,7 +48,7 @@ internal class OtherClientNoticeProcessor : MixedNoticeProcessor() {
|
||||
* @see OtherClientOnlineEvent
|
||||
* @see OtherClientOfflineEvent
|
||||
*/
|
||||
override suspend fun PipelineContext.processImpl(data: RequestPushStatus) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: RequestPushStatus) {
|
||||
markAsConsumed()
|
||||
bot.components[ContactUpdater].otherClientsLock.withLock {
|
||||
val instanceInfo = data.vecInstanceList?.firstOrNull()
|
||||
@ -103,7 +103,7 @@ internal class OtherClientNoticeProcessor : MixedNoticeProcessor() {
|
||||
/**
|
||||
* @see OtherClientMessageEvent
|
||||
*/
|
||||
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
if (msgHead.msgType != 529) return
|
||||
|
||||
// top_package/awbk.java:3765
|
||||
|
@ -13,8 +13,8 @@ import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.internal.contact.*
|
||||
import net.mamoe.mirai.internal.getGroupByUin
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext.Companion.fromSync
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.fromSync
|
||||
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
|
||||
import net.mamoe.mirai.internal.network.components.SsoProcessor
|
||||
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor
|
||||
@ -35,7 +35,7 @@ import net.mamoe.mirai.utils.context
|
||||
* @see GroupTempMessageSyncEvent
|
||||
*/
|
||||
internal class PrivateMessageProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
|
||||
override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) = data.context {
|
||||
markAsConsumed()
|
||||
if (msgHead.fromUin == bot.id && fromSync) {
|
||||
// Bot send message to himself? or from other client? I am not the implementer.
|
||||
@ -67,7 +67,7 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type
|
||||
|
||||
}
|
||||
|
||||
private suspend fun PipelineContext.handlePrivateMessage(
|
||||
private suspend fun NoticePipelineContext.handlePrivateMessage(
|
||||
data: MsgComm.Msg,
|
||||
user: AbstractUser,
|
||||
) = data.context {
|
||||
|
@ -414,7 +414,7 @@ internal class ImMsgBody : ProtoBuf {
|
||||
|
||||
@Serializable
|
||||
internal class ExtraInfo(
|
||||
@ProtoNumber(1) @JvmField val nick: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoNumber(1) @JvmField val nick: String = "",
|
||||
@ProtoNumber(2) @JvmField val groupCard: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoNumber(3) @JvmField val level: Int = 0,
|
||||
@ProtoNumber(4) @JvmField val flags: Int = 0,
|
||||
|
@ -22,8 +22,8 @@ import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.MultiPacket
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.processPacketThroughPipeline
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext.Companion.KEY_FROM_SYNC
|
||||
import net.mamoe.mirai.internal.network.components.SyncController.Companion.syncController
|
||||
import net.mamoe.mirai.internal.network.components.syncGetMessage
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
|
@ -12,8 +12,8 @@ package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.processPacketThroughPipeline
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext.Companion.KEY_FROM_SYNC
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
||||
|
@ -45,9 +45,9 @@ internal class MockBotBuilder(
|
||||
}
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
internal fun MockBot(conf: MockBotBuilder.() -> Unit = {}): QQAndroidBot {
|
||||
internal fun MockBot(account: BotAccount = MockAccount, conf: MockBotBuilder.() -> Unit = {}): QQAndroidBot {
|
||||
return MockBotBuilder(MockConfiguration.copy()).apply(conf).run {
|
||||
object : QQAndroidBot(MockAccount, this.conf) {
|
||||
object : QQAndroidBot(account, this.conf) {
|
||||
override fun createBotLevelComponents(): ConcurrentComponentStorage {
|
||||
return super.createBotLevelComponents().apply {
|
||||
val componentsProvider = additionalComponentsProvider
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.internal.network.framework
|
||||
|
||||
import net.mamoe.mirai.internal.BotAccount
|
||||
import net.mamoe.mirai.internal.MockAccount
|
||||
import net.mamoe.mirai.internal.MockConfiguration
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
@ -43,8 +44,10 @@ internal sealed class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abstr
|
||||
abstract val factory: NetworkHandlerFactory<H>
|
||||
abstract val network: H
|
||||
|
||||
var bot: QQAndroidBot by lateinitMutableProperty {
|
||||
object : QQAndroidBot(MockAccount, MockConfiguration.copy()) {
|
||||
var bot: QQAndroidBot by lateinitMutableProperty { createBot() }
|
||||
|
||||
protected open fun createBot(account: BotAccount = MockAccount): QQAndroidBot {
|
||||
return object : QQAndroidBot(account, MockConfiguration.copy()) {
|
||||
override fun createBotLevelComponents(): ConcurrentComponentStorage =
|
||||
super.createBotLevelComponents().apply { setAll(overrideComponents) }
|
||||
|
||||
|
182
mirai-core/src/commonTest/kotlin/notice/Desensitizer.kt
Normal file
182
mirai-core/src/commonTest/kotlin/notice/Desensitizer.kt
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.notice
|
||||
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.internal.utils.codegen.*
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import net.mamoe.yamlkt.YamlBuilder
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
private val logger: MiraiLogger by lazy { MiraiLogger.Factory.create(Desensitizer::class) }
|
||||
|
||||
internal class Desensitizer private constructor(
|
||||
val rules: Map<String, String>,
|
||||
) {
|
||||
fun desensitize(value: String): String {
|
||||
return rules.entries.fold(value) { acc, entry ->
|
||||
acc.replace(entry.key, entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
fun desensitize(value: ByteArray): ByteArray {
|
||||
return desensitize(value.toUHexString()).hexToBytes()
|
||||
}
|
||||
|
||||
fun desensitize(value: Array<Byte>): Array<Byte> {
|
||||
return desensitize(value.toUHexString()).hexToBytes().toTypedArray()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val instance by lateinitMutableProperty {
|
||||
create(
|
||||
run<Map<String, String>> {
|
||||
|
||||
val filename =
|
||||
systemProp("mirai.network.recording.desensitization.filepath", "local.desensitization.yml")
|
||||
|
||||
val file =
|
||||
Thread.currentThread().contextClassLoader.getResource(filename)
|
||||
?: Thread.currentThread().contextClassLoader.getResource("recording/configs/$filename")
|
||||
?: error("Could not find desensitization configuration!")
|
||||
|
||||
format.decodeFromString(file.readText())
|
||||
}.also {
|
||||
logger.info { "Loaded ${it.size} desensitization rules." }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loaded from local.desensitization.yml
|
||||
*/
|
||||
val local get() = instance
|
||||
|
||||
fun desensitize(string: String): String = instance.desensitize(string)
|
||||
|
||||
|
||||
fun ConstructorCallCodegenFacade.generateAndDesensitize(
|
||||
value: Any?,
|
||||
type: KType,
|
||||
desensitizer: Desensitizer = instance,
|
||||
): String {
|
||||
val a = analyze(value, type).apply {
|
||||
accept(DesensitizationVisitor(desensitizer))
|
||||
}
|
||||
return generate(a)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified T> ConstructorCallCodegenFacade.generateAndDesensitize(
|
||||
value: T,
|
||||
desensitizer: Desensitizer = instance,
|
||||
): String = generateAndDesensitize(value, typeOf<T>(), desensitizer)
|
||||
|
||||
|
||||
fun create(rules: Map<String, String>): Desensitizer {
|
||||
val map = HashMap<String, String>()
|
||||
map.putAll(rules)
|
||||
|
||||
fun addExtraRulesForString(value: String, replacement: String) {
|
||||
// in proto, strings have lengths field, we must ensure that their lengths are intact.
|
||||
|
||||
when {
|
||||
value.length > replacement.length -> {
|
||||
map[value.toByteArray().toUHexString()] =
|
||||
(replacement + "0".repeat(value.length - replacement.length)).toByteArray()
|
||||
.toUHexString() // fix it to the same length
|
||||
}
|
||||
value.length < replacement.length -> {
|
||||
error("Replacement '$replacement' must not be longer than '$value'")
|
||||
}
|
||||
else -> {
|
||||
map.putIfAbsent(value.toByteArray().toUHexString(), replacement.toByteArray().toUHexString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addExtraRulesForNumber(value: Long, replacement: Long) {
|
||||
map.putIfAbsent(value.toString(), replacement.toString())
|
||||
|
||||
// 某些地方会 readLong, readInt, desensitizer visit 不到这些目标
|
||||
map.putIfAbsent(value.toByteArray().toUHexString(), replacement.toByteArray().toUHexString())
|
||||
|
||||
if (value in Int.MIN_VALUE.toLong()..UInt.MAX_VALUE.toLong()
|
||||
&& replacement in Int.MIN_VALUE.toLong()..UInt.MAX_VALUE.toLong()
|
||||
) {
|
||||
map.putIfAbsent(
|
||||
value.toInt().toByteArray().toUHexString(),
|
||||
replacement.toInt().toByteArray().toUHexString()
|
||||
)
|
||||
}
|
||||
// 不需要处理 proto, 所有 proto 都会被反序列化为结构类型由 desensitizer 处理
|
||||
}
|
||||
|
||||
rules.forEach { (t, u) ->
|
||||
if (t.toLongOrNull() != null && u.toLongOrNull() != null) {
|
||||
addExtraRulesForNumber(t.toLong(), u.toLong())
|
||||
addExtraRulesForNumber(
|
||||
Mirai.calculateGroupUinByGroupCode(t.toLong()),
|
||||
Mirai.calculateGroupUinByGroupCode(u.toLong())
|
||||
) // putIfAbsent, code prevails
|
||||
}
|
||||
|
||||
addExtraRulesForString(t, u)
|
||||
}
|
||||
|
||||
return Desensitizer(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val format = Yaml {
|
||||
// one-line
|
||||
classSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
mapSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
listSerialization = YamlBuilder.ListSerialization.FLOW_SEQUENCE
|
||||
stringSerialization = YamlBuilder.StringSerialization.DOUBLE_QUOTATION
|
||||
encodeDefaultValues = false
|
||||
}
|
||||
|
||||
|
||||
private class DesensitizationVisitor(
|
||||
private val desensitizer: Desensitizer,
|
||||
) : ValueDescVisitor {
|
||||
override fun visitPlain(desc: PlainValueDesc) {
|
||||
desc.value = desensitizer.desensitize(desc.value)
|
||||
}
|
||||
|
||||
override fun visitObjectArray(desc: ObjectArrayValueDesc) {
|
||||
if (desc.arrayType.arguments.first().type?.classifier == Byte::class) { // variance is ignored
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
desc.value = desensitizer.desensitize(desc.value as Array<Byte>)
|
||||
} else {
|
||||
for (element in desc.elements) {
|
||||
element.accept(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCollection(desc: CollectionValueDesc) {
|
||||
for (element in desc.elements) {
|
||||
element.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc) {
|
||||
if (desc.value is ByteArray) {
|
||||
desc.value = desensitizer.desensitize(desc.value as ByteArray)
|
||||
}
|
||||
}
|
||||
}
|
@ -12,144 +12,39 @@ package net.mamoe.mirai.internal.notice
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.serializer
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline
|
||||
import net.mamoe.mirai.internal.network.components.PipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.ProcessResult
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
|
||||
import net.mamoe.mirai.internal.utils._miraiContentToString
|
||||
import net.mamoe.mirai.internal.notice.Desensitizer.Companion.generateAndDesensitize
|
||||
import net.mamoe.mirai.internal.utils.codegen.ConstructorCallCodegenFacade
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import net.mamoe.yamlkt.YamlBuilder
|
||||
import kotlin.reflect.full.createType
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.info
|
||||
|
||||
|
||||
/**
|
||||
* How to inject recorder?
|
||||
* ### How to use recorder?
|
||||
*
|
||||
* 0. Configure desensitization. See mirai-core/src/commonTest/recording/configs/desensitization.yml
|
||||
* 1. Inject the recorder as follows:
|
||||
*
|
||||
* ```
|
||||
* bot.components[NoticeProcessorPipeline].registerProcessor(recorder)
|
||||
* ```
|
||||
*
|
||||
* 2. Do something
|
||||
* 3. Recorded values are shown in logs. Check 'decoded' to ensure that all sensitive values are replaced.
|
||||
*/
|
||||
internal class RecordingNoticeProcessor : SimpleNoticeProcessor<ProtocolStruct>(type()) {
|
||||
private val id = atomic(0)
|
||||
private val lock = Mutex()
|
||||
|
||||
override suspend fun PipelineContext.processImpl(data: ProtocolStruct) {
|
||||
override suspend fun NoticePipelineContext.processImpl(data: ProtocolStruct) {
|
||||
lock.withLock {
|
||||
id.getAndDecrement()
|
||||
logger.info { "Recorded #${id.value} ${data::class.simpleName}" }
|
||||
val serial = serialize(this, data)
|
||||
logger.info { "original: $serial" }
|
||||
logger.info { "desensitized: " + desensitize(serial) }
|
||||
logger.info { "decoded: " + deserialize(desensitize(serial)).struct._miraiContentToString() }
|
||||
logger.info { "Desensitized: \n\n\u001B[0m" + ConstructorCallCodegenFacade.generateAndDesensitize(data) + "\n\n" }
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RecordNode(
|
||||
val structType: String,
|
||||
val struct: String,
|
||||
val attributes: Map<String, String>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DeserializedRecord(
|
||||
val attributes: TypeSafeMap,
|
||||
val struct: ProtocolStruct
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val logger = MiraiLogger.Factory.create(RecordingNoticeProcessor::class)
|
||||
|
||||
private val yaml = Yaml {
|
||||
// one-line
|
||||
classSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
mapSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
listSerialization = YamlBuilder.ListSerialization.FLOW_SEQUENCE
|
||||
stringSerialization = YamlBuilder.StringSerialization.DOUBLE_QUOTATION
|
||||
encodeDefaultValues = false
|
||||
}
|
||||
|
||||
fun serialize(context: PipelineContext, data: ProtocolStruct): String {
|
||||
return serialize(context.attributes.toMap(), data)
|
||||
}
|
||||
|
||||
fun serialize(attributes: Map<String, @Contextual Any?>, data: ProtocolStruct): String {
|
||||
return yaml.encodeToString(
|
||||
RecordNode(
|
||||
data::class.java.name,
|
||||
yaml.encodeToString(data),
|
||||
attributes.mapValues { yaml.encodeToString(it.value) })
|
||||
)
|
||||
}
|
||||
|
||||
fun deserialize(string: String): DeserializedRecord {
|
||||
val (type, struct, attributes) = yaml.decodeFromString(RecordNode.serializer(), string)
|
||||
val serializer = serializer(Class.forName(type).kotlin.createType())
|
||||
return DeserializedRecord(
|
||||
TypeSafeMap(attributes.mapValues { yaml.decodeAnyFromString(it.value) }),
|
||||
yaml.decodeFromString(serializer, struct).cast()
|
||||
)
|
||||
}
|
||||
|
||||
private val desensitizer by lateinitMutableProperty {
|
||||
Desensitizer.create(
|
||||
run<Map<String, String>> {
|
||||
|
||||
val filename =
|
||||
systemProp("mirai.network.recording.desensitization.filepath", "local.desensitization.yml")
|
||||
|
||||
val file =
|
||||
Thread.currentThread().contextClassLoader.getResource(filename)
|
||||
?: Thread.currentThread().contextClassLoader.getResource("recording/configs/$filename")
|
||||
?: error("Could not find desensitization configuration!")
|
||||
|
||||
yaml.decodeFromString(file.readText())
|
||||
}.also {
|
||||
logger.info { "Loaded ${it.size} desensitization rules." }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun desensitize(string: String): String = desensitizer.desensitize(string)
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun NoticeProcessorPipeline.processRecording(
|
||||
bot: QQAndroidBot,
|
||||
record: RecordingNoticeProcessor.DeserializedRecord
|
||||
): ProcessResult {
|
||||
return this.process(bot, record.struct, record.attributes)
|
||||
}
|
||||
|
||||
internal class Desensitizer private constructor(
|
||||
val rules: Map<String, String>,
|
||||
) {
|
||||
companion object {
|
||||
fun create(rules: Map<String, String>): Desensitizer {
|
||||
val map = HashMap<String, String>()
|
||||
map.putAll(rules)
|
||||
rules.forEach { (t, u) ->
|
||||
if (t.toLongOrNull() != null && u.toLongOrNull() != null) {
|
||||
map.putIfAbsent(
|
||||
Mirai.calculateGroupUinByGroupCode(t.toLong()).toString(),
|
||||
Mirai.calculateGroupUinByGroupCode(u.toLong()).toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
return Desensitizer(rules)
|
||||
}
|
||||
}
|
||||
|
||||
fun desensitize(value: String): String {
|
||||
return rules.entries.fold(value) { acc, entry ->
|
||||
acc.replace(entry.key, entry.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val logger: MiraiLogger by lazy { MiraiLogger.Factory.create(RecordingNoticeProcessor::class) }
|
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.notice.processors
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.internal.BotAccount
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.GroupImpl
|
||||
import net.mamoe.mirai.internal.contact.NormalMemberImpl
|
||||
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
|
||||
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
||||
import net.mamoe.mirai.internal.network.components.LoggingPacketHandlerAdapter
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.noticeProcessorPipeline
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipelineImpl
|
||||
import net.mamoe.mirai.internal.network.components.PacketLoggingStrategyImpl
|
||||
import net.mamoe.mirai.internal.network.components.ProcessResult
|
||||
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.utils.TypeSafeMap
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import net.mamoe.mirai.utils.hexToUBytes
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* To add breakpoint, see [NoticeProcessorPipelineImpl.process]
|
||||
*/
|
||||
internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), GroupExtensions {
|
||||
init {
|
||||
System.setProperty("mirai.network.notice.pipeline.log.full", "true")
|
||||
}
|
||||
|
||||
protected object UseTestContext {
|
||||
val EMPTY_BYTE_ARRAY get() = net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||
fun String.hexToBytes() = hexToUBytes().toByteArray()
|
||||
}
|
||||
|
||||
protected suspend inline fun use(
|
||||
attributes: TypeSafeMap = TypeSafeMap(),
|
||||
block: UseTestContext.() -> ProtocolStruct
|
||||
): ProcessResult {
|
||||
val handler = LoggingPacketHandlerAdapter(PacketLoggingStrategyImpl(bot), bot.logger)
|
||||
return bot.components.noticeProcessorPipeline.process(bot, block(UseTestContext), attributes).also { list ->
|
||||
for (packet in list) {
|
||||
handler.handlePacket(IncomingPacket("test", packet))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val properties = Properties().apply {
|
||||
load(Thread.currentThread().contextClassLoader.getResourceAsStream("recording/data/notice/NoticeProcessorTestData.properties"))
|
||||
}
|
||||
|
||||
fun setBot(id: Long): QQAndroidBot {
|
||||
bot = createBot(BotAccount(id, "a"))
|
||||
return bot
|
||||
}
|
||||
}
|
||||
|
||||
internal interface GroupExtensions {
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
fun GroupInfo(
|
||||
uin: Long,
|
||||
owner: Long,
|
||||
groupCode: Long,
|
||||
memo: String = "",
|
||||
name: String,
|
||||
allowMemberInvite: Boolean = false,
|
||||
allowAnonymousChat: Boolean = false,
|
||||
autoApprove: Boolean = false,
|
||||
confessTalk: Boolean = false,
|
||||
muteAll: Boolean = false,
|
||||
botMuteTimestamp: Int = 0,
|
||||
): GroupInfoImpl =
|
||||
GroupInfoImpl(
|
||||
uin, owner, groupCode, memo, name,
|
||||
allowMemberInvite, allowAnonymousChat, autoApprove, confessTalk, muteAll,
|
||||
botMuteTimestamp
|
||||
)
|
||||
|
||||
fun Bot.addGroup(group: Group) {
|
||||
groups.delegate.add(group)
|
||||
}
|
||||
|
||||
fun Bot.addFriend(friend: Friend) {
|
||||
friends.delegate.add(friend)
|
||||
}
|
||||
|
||||
fun Group.addMember(member: NormalMember) {
|
||||
members.delegate.add(member)
|
||||
}
|
||||
|
||||
|
||||
fun Bot.addGroup(
|
||||
id: Long,
|
||||
owner: Long,
|
||||
botPermission: MemberPermission = MemberPermission.MEMBER,
|
||||
memo: String = "",
|
||||
name: String = "Test Group",
|
||||
allowMemberInvite: Boolean = false,
|
||||
allowAnonymousChat: Boolean = false,
|
||||
autoApprove: Boolean = false,
|
||||
confessTalk: Boolean = false,
|
||||
muteAll: Boolean = false,
|
||||
botMuteTimestamp: Int = 0,
|
||||
): GroupImpl {
|
||||
val impl = GroupImpl(
|
||||
bot.cast(), coroutineContext, id,
|
||||
GroupInfo(
|
||||
Mirai.calculateGroupUinByGroupCode(id), owner, id, memo, name, allowMemberInvite,
|
||||
allowAnonymousChat, autoApprove, confessTalk, muteAll, botMuteTimestamp
|
||||
),
|
||||
ContactList(),
|
||||
)
|
||||
addGroup(impl)
|
||||
impl.botAsMember = impl.addMember(bot.id, nick = bot.nick, permission = botPermission)
|
||||
return impl
|
||||
}
|
||||
|
||||
fun Bot.addGroup(
|
||||
id: Long,
|
||||
info: GroupInfoImpl,
|
||||
botPermission: MemberPermission = MemberPermission.MEMBER,
|
||||
): Group {
|
||||
val impl = GroupImpl(
|
||||
bot.cast(), coroutineContext, id, info,
|
||||
ContactList(),
|
||||
)
|
||||
addGroup(impl)
|
||||
impl.botAsMember = impl.addMember(bot.id, nick = bot.nick, permission = botPermission)
|
||||
return impl
|
||||
}
|
||||
|
||||
fun Group.addMember(
|
||||
id: Long,
|
||||
nick: String,
|
||||
permission: MemberPermission,
|
||||
remark: String = "",
|
||||
nameCard: String = "",
|
||||
specialTitle: String = "",
|
||||
muteTimestamp: Int = 0,
|
||||
anonymousId: String? = null,
|
||||
joinTimestamp: Int = currentTimeSeconds().toInt(),
|
||||
lastSpeakTimestamp: Int = 0,
|
||||
isOfficialBot: Boolean = false,
|
||||
): NormalMemberImpl {
|
||||
val member = NormalMemberImpl(
|
||||
this.cast(), this.coroutineContext,
|
||||
MemberInfoImpl(
|
||||
id, nick, permission, remark, nameCard,
|
||||
specialTitle, muteTimestamp, anonymousId, joinTimestamp, lastSpeakTimestamp, isOfficialBot
|
||||
)
|
||||
)
|
||||
members.delegate.add(
|
||||
member
|
||||
)
|
||||
return member
|
||||
}
|
||||
|
||||
fun Group.addMember(
|
||||
id: Long,
|
||||
info: MemberInfoImpl,
|
||||
): Group {
|
||||
members.delegate.add(NormalMemberImpl(this.cast(), this.coroutineContext, info))
|
||||
return this
|
||||
}
|
||||
}
|
@ -11,22 +11,25 @@ package net.mamoe.mirai.internal.notice.test
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import net.mamoe.mirai.internal.MockBot
|
||||
import net.mamoe.mirai.internal.network.components.AbstractPipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.AbstractNoticePipelineContext
|
||||
import net.mamoe.mirai.internal.network.components.ProcessResult
|
||||
import net.mamoe.mirai.internal.notice.Desensitizer
|
||||
import net.mamoe.mirai.internal.notice.RecordingNoticeProcessor
|
||||
import net.mamoe.mirai.internal.notice.Desensitizer.Companion.generateAndDesensitize
|
||||
import net.mamoe.mirai.internal.test.AbstractTest
|
||||
import net.mamoe.mirai.internal.utils.codegen.ConstructorCallCodegenFacade
|
||||
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
|
||||
import net.mamoe.mirai.utils.MutableTypeSafeMap
|
||||
import net.mamoe.mirai.utils.TypeSafeMap
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import net.mamoe.yamlkt.YamlBuilder
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class RecordingNoticeProcessorTest : AbstractTest() {
|
||||
|
||||
class MyContext(attributes: TypeSafeMap) : AbstractPipelineContext(MockBot(), attributes) {
|
||||
class MyContext(attributes: TypeSafeMap) : AbstractNoticePipelineContext(MockBot(), attributes) {
|
||||
override suspend fun processAlso(data: ProtocolStruct, attributes: TypeSafeMap): ProcessResult {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@ -42,29 +45,23 @@ internal class RecordingNoticeProcessorTest : AbstractTest() {
|
||||
val context = MyContext(MutableTypeSafeMap(mapOf("test" to "value")))
|
||||
val struct = MyProtocolStruct("vvv")
|
||||
|
||||
val serialize = RecordingNoticeProcessor.serialize(context, struct)
|
||||
println(serialize)
|
||||
val deserialized = RecordingNoticeProcessor.deserialize(serialize)
|
||||
|
||||
assertEquals(context.attributes, deserialized.attributes)
|
||||
assertEquals(struct, deserialized.struct)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can read desensitization config`() {
|
||||
val text = Thread.currentThread().contextClassLoader.getResource("recording/configs/test.desensitization.yml")!!
|
||||
.readText()
|
||||
val desensitizer = Desensitizer.create(Yaml.decodeFromString(text))
|
||||
val serialize = ConstructorCallCodegenFacade.generateAndDesensitize(struct)
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"123456789" to "111",
|
||||
"987654321" to "111"
|
||||
), desensitizer.rules
|
||||
"""
|
||||
net.mamoe.mirai.internal.notice.test.RecordingNoticeProcessorTest.MyProtocolStruct(
|
||||
value="vvv",
|
||||
)
|
||||
""".trimIndent(),
|
||||
serialize
|
||||
)
|
||||
// val deserialized = KotlinScriptExternalDependencies
|
||||
//
|
||||
// assertEquals(context.attributes, deserialized.attributes)
|
||||
// assertEquals(struct, deserialized.struct)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test desensitization`() {
|
||||
fun `test plain desensitization`() {
|
||||
val text = Thread.currentThread().contextClassLoader.getResource("recording/configs/test.desensitization.yml")!!
|
||||
.readText()
|
||||
val desensitizer = Desensitizer.create(Yaml.decodeFromString(text))
|
||||
@ -82,6 +79,48 @@ internal class RecordingNoticeProcessorTest : AbstractTest() {
|
||||
""".trim()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
data class TestProto(
|
||||
@ProtoNumber(1) val proto: Proto
|
||||
) : ProtocolStruct {
|
||||
@Serializable
|
||||
data class Proto(
|
||||
@ProtoNumber(1) val int: Int
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ByteArrayWrapper(
|
||||
val value: ByteArray
|
||||
)
|
||||
|
||||
val format = Yaml {
|
||||
// one-line
|
||||
classSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
mapSerialization = YamlBuilder.MapSerialization.FLOW_MAP
|
||||
listSerialization = YamlBuilder.ListSerialization.FLOW_SEQUENCE
|
||||
stringSerialization = YamlBuilder.StringSerialization.DOUBLE_QUOTATION
|
||||
encodeDefaultValues = false
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test long as byte array desensitization`() {
|
||||
val text = Thread.currentThread().contextClassLoader.getResource("recording/configs/test.desensitization.yml")!!
|
||||
.readText()
|
||||
val desensitizer = Desensitizer.create(Yaml.decodeFromString(text))
|
||||
|
||||
val proto = TestProto(TestProto.Proto(123456789))
|
||||
|
||||
assertEquals(
|
||||
TestProto(TestProto.Proto(111)),
|
||||
format.decodeFromString(
|
||||
TestProto.serializer(),
|
||||
desensitizer.desensitize(format.encodeToString(TestProto.serializer(), proto))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@
|
||||
#
|
||||
# Format:
|
||||
# ```
|
||||
# <sensitive value>: <replacer>
|
||||
# <sensitive value>: <replacement>
|
||||
# ```
|
||||
#
|
||||
# If key is a number, its group uin counterpart will also be processed, with calculated replacer.
|
||||
# WARNING: Ensure the <replacement> is not longer than <sensitive value>.
|
||||
#
|
||||
# For example, if your account id is 147258369, you may add:
|
||||
# ```
|
||||
|
52
mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt
Normal file
52
mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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 kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotFactory
|
||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline
|
||||
import net.mamoe.mirai.internal.notice.Desensitizer
|
||||
import net.mamoe.mirai.internal.notice.RecordingNoticeProcessor
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.readResource
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
@Serializable
|
||||
data class LocalAccount(
|
||||
val id: Long,
|
||||
val password: String
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
bot.components[NoticeProcessorPipeline].registerProcessor(RecordingNoticeProcessor())
|
||||
|
||||
bot.login()
|
||||
|
||||
bot.join()
|
||||
}
|
2
mirai-core/src/jvmTest/resources/account.yml
Normal file
2
mirai-core/src/jvmTest/resources/account.yml
Normal file
@ -0,0 +1,2 @@
|
||||
id: 123
|
||||
password: ""
|
Loading…
Reference in New Issue
Block a user