Migrate more transformers to NoticeProcessorPipeline

This commit is contained in:
Him188 2021-08-13 18:53:51 +08:00
parent e097c5ab9d
commit edf1bc9a2e
21 changed files with 427 additions and 272 deletions

View File

@ -12,9 +12,9 @@
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.bundling.Jar
import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@ -144,7 +144,9 @@ val experimentalAnnotations = arrayOf(
"net.mamoe.mirai.message.data.ExperimentalMessageKey",
"net.mamoe.mirai.console.ConsoleFrontEndImplementation",
"net.mamoe.mirai.console.util.ConsoleInternalApi",
"net.mamoe.mirai.console.util.ConsoleExperimentalApi"
"net.mamoe.mirai.console.util.ConsoleExperimentalApi",
"kotlinx.io.core.internal.DangerousInternalIoApi",
)
fun Project.configureKotlinExperimentalUsages() {

View File

@ -109,4 +109,7 @@ public fun String.truncated(length: Int, truncated: String = "..."): String {
public inline fun <T> T.context(block: T.() -> Unit) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
}
public fun assertUnreachable(hint: String? = null): Nothing =
error("This clause should not be reached. " + hint.orEmpty())

View File

@ -37,8 +37,15 @@ import net.mamoe.mirai.internal.network.handler.state.StateObserver
import net.mamoe.mirai.internal.network.handler.state.safe
import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException
import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory
import net.mamoe.mirai.internal.network.notice.*
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.GroupOrMemberListNoticeProcessor
import net.mamoe.mirai.internal.network.notice.group.GroupRecallProcessor
import net.mamoe.mirai.internal.network.notice.priv.FriendNoticeProcessor
import net.mamoe.mirai.internal.network.notice.priv.OtherClientNoticeProcessor
import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageNoticeProcessor
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.utils.subLogger
import net.mamoe.mirai.utils.BotConfiguration
@ -156,13 +163,16 @@ internal open class QQAndroidBot constructor(
set(
NoticeProcessorPipeline,
NoticeProcessorPipelineImpl.create(
MsgInfoDecoder(pipelineLogger),
FriendNoticeProcessor(pipelineLogger),
GroupListNoticeProcessor(pipelineLogger),
GroupMessageProcessor(),
MsgInfoDecoder(pipelineLogger.subLogger("MsgInfoDecoder")),
GroupNotificationDecoder(),
FriendNoticeProcessor(pipelineLogger.subLogger("FriendNoticeProcessor")),
GroupOrMemberListNoticeProcessor(pipelineLogger.subLogger("GroupOrMemberListNoticeProcessor")),
GroupMessageProcessor(pipelineLogger.subLogger("GroupMessageProcessor")),
PrivateMessageNoticeProcessor(),
OtherClientNoticeProcessor(),
UnconsumedNoticesAlerter(pipelineLogger),
UnconsumedNoticesAlerter(pipelineLogger.subLogger("UnconsumedNoticesAlerter")),
GroupRecallProcessor()
)
)

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.utils.cast
import kotlin.coroutines.CoroutineContext
internal abstract class AbstractMember(
internal sealed class AbstractMember(
final override val group: GroupImpl,
parentCoroutineContext: CoroutineContext,
memberInfo: MemberInfo,

View File

@ -57,7 +57,7 @@ internal val User.correspondingMessageSourceKind
else -> error("Unknown user: ${this::class.qualifiedName}")
}
internal abstract class AbstractUser(
internal sealed class AbstractUser(
bot: QQAndroidBot,
parentCoroutineContext: CoroutineContext,
userInfo: UserInfo,

View File

@ -14,16 +14,14 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.nextEventOrNull
import net.mamoe.mirai.internal.MiraiImpl
import net.mamoe.mirai.internal.asQQAndroidBot
import net.mamoe.mirai.internal.getMiraiImpl
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.components.MessageSvcSyncer
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.notice.GroupMessageProcessor.SendGroupMessageReceipt
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.SendGroupMessageReceipt
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement

View File

@ -28,6 +28,10 @@ import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.asQQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.OnlineAnnouncementImpl
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.deleteGroupAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getGroupAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.getRawGroupAnnouncements
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.sendGroupAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement
import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toGroupAnnouncement
import net.mamoe.mirai.internal.network.highway.ChannelKind
@ -37,6 +41,8 @@ import net.mamoe.mirai.internal.network.psKey
import net.mamoe.mirai.internal.network.sKey
import net.mamoe.mirai.internal.utils.io.writeResource
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.Either.Companion.onLeft
import net.mamoe.mirai.utils.Either.Companion.rightOrNull
import java.util.stream.Stream
internal class AnnouncementsImpl(

View File

@ -20,7 +20,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.asyncFromEventOrNull
import net.mamoe.mirai.internal.network.notice.GroupMessageProcessor.SendGroupMessageReceipt
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.SendGroupMessageReceipt
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.SourceMsg

View File

@ -15,6 +15,7 @@ import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.ParseErrorPacket
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.notice.decoders.DecodedNotifyMsgBody
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
@ -32,7 +33,6 @@ import net.mamoe.mirai.utils.toDebugString
import net.mamoe.mirai.utils.uncheckedCast
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KClass
internal typealias ProcessResult = Collection<Packet>
@ -98,8 +98,8 @@ internal interface PipelineContext {
val collected: MutableProcessResult
// DSL to simplify some expressions
operator fun MutableProcessResult.plusAssign(packet: Packet) {
collect(packet)
operator fun MutableProcessResult.plusAssign(packet: Packet?) {
if (packet != null) collect(packet)
}
@ -114,11 +114,11 @@ internal interface PipelineContext {
fun collect(packets: Iterable<Packet>)
/**
* Fire the [data] into the processor pipeline.
* Fire the [data] into the processor pipeline, and collect the results to current [collected].
*
* @return result collected from processors. This would also have been collected to this context (where you call [fire]).
* @return result collected from processors. This would also have been collected to this context (where you call [processAlso]).
*/
suspend fun fire(data: ProtocolStruct): ProcessResult
suspend fun processAlso(data: ProtocolStruct): ProcessResult
companion object {
val KEY_FROM_SYNC = TypeKey<Boolean>("fromSync")
@ -129,7 +129,10 @@ internal interface PipelineContext {
internal inline val PipelineContext.context get() = this
internal open class NoticeProcessorPipelineImpl private constructor() : NoticeProcessorPipeline {
private val processors = CopyOnWriteArrayList<NoticeProcessor>()
/**
* Must be ordered
*/
private val processors = ConcurrentLinkedQueue<NoticeProcessor>()
override fun registerProcessor(processor: NoticeProcessor) {
processors.add(processor)
@ -141,7 +144,7 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
) : PipelineContext {
private val consumers: Stack<NoticeProcessor> = Stack()
override val isConsumed: Boolean = consumers.isNotEmpty()
override val isConsumed: Boolean get() = consumers.isNotEmpty()
override fun NoticeProcessor.markAsConsumed() {
consumers.push(this)
}
@ -162,7 +165,7 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
this.collected.data.addAll(packets)
}
override suspend fun fire(data: ProtocolStruct): ProcessResult {
override suspend fun processAlso(data: ProtocolStruct): ProcessResult {
return process(bot, data, attributes)
}
}
@ -193,7 +196,6 @@ internal open class NoticeProcessorPipelineImpl private constructor() : NoticePr
companion object {
fun createEmpty(): NoticeProcessorPipelineImpl = NoticeProcessorPipelineImpl()
fun create(vararg processors: NoticeProcessor): NoticeProcessorPipelineImpl =
NoticeProcessorPipelineImpl().apply {
for (processor in processors) {
@ -214,9 +216,9 @@ internal interface NoticeProcessor {
suspend fun process(context: PipelineContext, data: Any?)
}
internal abstract class AnyNoticeProcessor : SimpleNoticeProcessor<Any>(type())
internal abstract class AnyNoticeProcessor : SimpleNoticeProcessor<ProtocolStruct>(type())
internal abstract class SimpleNoticeProcessor<T : Any>(
internal abstract class SimpleNoticeProcessor<in T : ProtocolStruct>(
private val type: KClass<T>,
) : NoticeProcessor {
@ -239,7 +241,7 @@ internal abstract class MsgCommonMsgProcessor : SimpleNoticeProcessor<MsgComm.Ms
}
internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
final override suspend fun PipelineContext.processImpl(data: Any) {
final override suspend fun PipelineContext.processImpl(data: ProtocolStruct) {
when (data) {
is PbMsgInfo -> processImpl(data)
is MsgOnlinePush.PbPushMsg -> processImpl(data)
@ -248,6 +250,7 @@ internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
is MsgType0x2DC -> processImpl(data)
is Structmsg.StructMsg -> processImpl(data)
is RequestPushStatus -> processImpl(data)
is DecodedNotifyMsgBody -> processImpl(data)
}
}
@ -258,4 +261,6 @@ internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
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 PipelineContext.processImpl(data: DecodedNotifyMsgBody) {}
}

View File

@ -27,7 +27,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
internal interface NewContactSupport {
internal interface NewContactSupport { // can be a marker interface when context receivers are available.
fun MsgComm.Msg.getNewMemberInfo(): MemberInfoImpl {
return MemberInfoImpl(

View File

@ -0,0 +1,34 @@
/*
* 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.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.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) {
when (data.kind) {
0x10 -> {
val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
processAlso(DecodedNotifyMsgBody(data.kind, data.group, proto))
}
}
}
}
internal data class DecodedNotifyMsgBody(
override val kind: Int,
override val group: GroupImpl,
override val buf: TroopTips0x857.NotifyMsgBody,
) : BaseMsgType0x2DC<TroopTips0x857.NotifyMsgBody>, ProtocolStruct

View File

@ -46,7 +46,7 @@ internal class MsgInfoDecoder(
if (!bot.syncController.syncOnlinePush(data)) return
when (data.shMsgType.toUShort().toInt()) {
// 528
0x210 -> fire(data.vMsg.loadAs(MsgType0x210.serializer()))
0x210 -> processAlso(data.vMsg.loadAs(MsgType0x210.serializer()))
// 732
0x2dc -> {
@ -57,7 +57,7 @@ internal class MsgInfoDecoder(
val kind = readByte().toInt()
discardExact(1)
fire(MsgType0x2DC(kind, group, this.readBytes()))
processAlso(MsgType0x2DC(kind, group, this.readBytes()))
}
}
else -> {
@ -67,8 +67,37 @@ internal class MsgInfoDecoder(
}
}
internal class MsgType0x2DC(
val kind: Int, // inner kind, read from vMsg
val group: GroupImpl,
val buf: ByteArray,
) : ProtocolStruct
internal interface BaseMsgType0x2DC<V> {
val kind: Int
val group: GroupImpl
val buf: V
fun Long.findMember() = group[this]
fun String.findMember() = this.toLongOrNull()?.let { group[it] }
}
internal data class MsgType0x2DC(
override val kind: Int, // inner kind, read from vMsg
override val group: GroupImpl,
override val buf: ByteArray,
) : ProtocolStruct, BaseMsgType0x2DC<ByteArray> {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MsgType0x2DC
if (kind != other.kind) return false
if (group != other.group) return false
if (!buf.contentEquals(other.buf)) return false
return true
}
override fun hashCode(): Int {
var result = kind
result = 31 * result + group.hashCode()
result = 31 * result + buf.contentHashCode()
return result
}
}

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
package net.mamoe.mirai.internal.network.notice.group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.event.AbstractEvent
@ -26,8 +26,8 @@ import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.PipelineContext
import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
import net.mamoe.mirai.internal.network.components.SyncController.Companion.syncController
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.notice.GroupMessageProcessor.MemberNick.Companion.generateMemberNickFromMember
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.MemberNick.Companion.generateMemberNickFromMember
import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageNoticeProcessor
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.MsgOnlinePush
@ -39,7 +39,9 @@ import net.mamoe.mirai.utils.*
/**
* Handles [GroupMessageEvent]. For private message events, see [PrivateMessageNoticeProcessor]
*/
internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPushMsg>(type()) {
internal class GroupMessageProcessor(
private val logger: MiraiLogger,
) : SimpleNoticeProcessor<MsgOnlinePush.PbPushMsg>(type()) {
internal data class SendGroupMessageReceipt(
val messageRandom: Int,
val sequenceId: Int,
@ -113,7 +115,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
nameCard = sender.generateMemberNickFromMember()
} else { // normal member chat
sender = group[msgHead.fromUin] ?: kotlin.run {
bot.network.logger.warning { "Failed to find member ${msgHead.fromUin} in group ${group.id}" }
logger.warning { "Failed to find member ${msgHead.fromUin} in group ${group.id}" }
return
}
nameCard = findSenderName(extraInfo, msgHead.groupInfo) ?: sender.generateMemberNickFromMember()

View File

@ -0,0 +1,213 @@
/*
* 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.network.notice.group
import kotlinx.io.core.readUInt
import kotlinx.io.core.readUShort
import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
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.notice.NewContactSupport
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._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.utils.context
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.read
internal class GroupNotificationProcessor : MixedNoticeProcessor(), NewContactSupport {
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {
when (data.kind) {
0x0C -> processMute(data)
0x0E -> processAllowAnonymousChat(data)
0x10 -> processAllowConfessTask(data)
0x14 -> processGrayTip(data)
}
}
/**
* @see MemberMuteEvent
* @see MemberUnmuteEvent
* @see GroupMuteAllEvent
* @see BotMuteEvent
* @see BotUnmuteEvent
*/
private fun PipelineContext.processMute(
data: MsgType0x2DC,
) = data.context {
fun handleMuteMemberPacket(
bot: QQAndroidBot,
group: GroupImpl,
operator: NormalMember,
target: Long,
timeSeconds: Int,
): Packet? {
if (target == 0L) {
val new = timeSeconds != 0
if (group.settings.isMuteAllField == new) {
return null
}
group.settings.isMuteAllField = new
return GroupMuteAllEvent(!new, new, group, operator)
}
if (target == bot.id) {
return when {
group.botMuteRemaining == timeSeconds -> null
timeSeconds == 0 || timeSeconds == 0xFFFF_FFFF.toInt() -> {
group.botAsMember.checkIsMemberImpl()._muteTimestamp = 0
BotUnmuteEvent(operator)
}
else -> {
group.botAsMember.checkIsMemberImpl()._muteTimestamp =
currentTimeSeconds().toInt() + timeSeconds
BotMuteEvent(timeSeconds, operator)
}
}
}
val member = group[target] ?: return null
member.checkIsMemberImpl()
if (member.muteTimeRemaining == timeSeconds) return null
member._muteTimestamp = currentTimeSeconds().toInt() + timeSeconds
return if (timeSeconds == 0) MemberUnmuteEvent(member, operator)
else MemberMuteEvent(member, timeSeconds, operator)
}
markAsConsumed()
buf.read {
val operatorUin = readUInt().toLong()
if (operatorUin == bot.id) return
val operator = group[operatorUin] ?: return
readUInt().toLong() // time
val length = readUShort().toInt()
repeat(length) {
val target = readUInt().toLong()
val timeSeconds = readUInt()
collected += handleMuteMemberPacket(bot, group, operator, target, timeSeconds.toInt())
}
}
}
/**
* @see GroupAllowAnonymousChatEvent
*/
private fun PipelineContext.processAllowAnonymousChat(
data: MsgType0x2DC,
) = data.context {
markAsConsumed()
buf.read {
val operator = group[readUInt().toLong()] ?: return
val new = readInt() == 0
if (group.settings.isAnonymousChatEnabledField == new) return
group.settings.isAnonymousChatEnabledField = new
collect(GroupAllowAnonymousChatEvent(!new, new, group, operator))
}
}
/**
* @see GroupAllowConfessTalkEvent
*/
private fun PipelineContext.processAllowConfessTask(
data: MsgType0x2DC,
) = data.context {
val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
markAsConsumed()
when (proto.optEnumType) {
1 -> {
val tipsInfo = proto.optMsgGraytips ?: return
val message = tipsInfo.optBytesContent.decodeToString()
// 机器人信息
when (tipsInfo.robotGroupOpt) {
// others
0 -> {
if (message.endsWith("群聊坦白说")) {
val new = when (message) {
"管理员已关闭群聊坦白说" -> false
"管理员已开启群聊坦白说" -> true
else -> {
bot.network.logger.debug { "Unknown server confess talk messages $message" }
return
}
}
collect(GroupAllowConfessTalkEvent(new, !new, group, false))
}
}
}
}
else -> markNotConsumed()
}
}
/**
* @see NudgeEvent
* @see MemberHonorChangeEvent
* @see GroupTalkativeChangeEvent
*/
private fun PipelineContext.processGrayTip(
data: MsgType0x2DC,
) = data.context {
val grayTip = buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), 1).optGeneralGrayTip
markAsConsumed()
when (grayTip?.templId) {
// 戳一戳
10043L, 1133L, 1132L, 1134L, 1135L, 1136L -> {
//预置数据,服务器将不会提供己方已知消息
val action = grayTip.msgTemplParam["action_str"].orEmpty()
val from = grayTip.msgTemplParam["uin_str1"]?.findMember() ?: group.botAsMember
val target = grayTip.msgTemplParam["uin_str2"]?.findMember() ?: group.botAsMember
val suffix = grayTip.msgTemplParam["suffix_str"].orEmpty()
collected += NudgeEvent(
from = if (from.id == bot.id) bot else from,
target = if (target.id == bot.id) bot else target,
action = action,
suffix = suffix,
subject = group,
)
}
// 龙王
10093L, 1053L, 1054L -> {
val now: NormalMember = grayTip.msgTemplParam["uin"]?.findMember() ?: group.botAsMember
val previous: NormalMember? = grayTip.msgTemplParam["uin_last"]?.findMember()
if (previous == null) {
collect(MemberHonorChangeEvent.Achieve(now, GroupHonorType.TALKATIVE))
} else {
collect(GroupTalkativeChangeEvent(group, now, previous))
collect(MemberHonorChangeEvent.Lose(previous, GroupHonorType.TALKATIVE))
collect(MemberHonorChangeEvent.Achieve(now, GroupHonorType.TALKATIVE))
}
}
else -> {
markNotConsumed()
bot.network.logger.debug {
"Unknown Transformers528 0x14 template\ntemplId=${grayTip?.templId}\nPermList=${grayTip?.msgTemplParam?._miraiContentToString()}"
}
}
}
}
}
internal operator fun List<TroopTips0x857.TemplParam>.get(name: String) = this.findLast { it.name == name }?.value

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
package net.mamoe.mirai.internal.network.notice.group
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
@ -25,16 +25,19 @@ 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.notice.decoders.MsgType0x2DC
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
import net.mamoe.mirai.internal.network.protocol.data.proto.*
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans
import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.parseToMessageDataList
import net.mamoe.mirai.internal.utils.toMemberInfo
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.context
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.read
@ -54,7 +57,7 @@ import net.mamoe.mirai.utils.read
* @see BotInvitedJoinGroupRequestEvent
* @see MemberJoinRequestEvent
*/
internal class GroupListNoticeProcessor(
internal class GroupOrMemberListNoticeProcessor(
private val logger: MiraiLogger,
) : MixedNoticeProcessor(), NewContactSupport {
@ -79,14 +82,14 @@ internal class GroupListNoticeProcessor(
* @see MemberJoinEvent.Invite
* @see MemberLeaveEvent.Quit
*/
override suspend fun PipelineContext.processImpl(data: MsgType0x2DC) = data.context {
if (data.kind != 0x10) return
val proto = data.buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), offset = 1)
override suspend fun PipelineContext.processImpl(data: DecodedNotifyMsgBody) = data.context {
val proto = data.buf
if (proto.optEnumType != 1) return
val tipsInfo = proto.optMsgGraytips ?: return
val message = tipsInfo.optBytesContent.decodeToString()
// 机器人信息
markAsConsumed()
when (tipsInfo.robotGroupOpt) {
// 添加
1 -> {
@ -107,13 +110,8 @@ internal class GroupListNoticeProcessor(
collect(MemberLeaveEvent.Quit(member))
}
}
else -> {
logger.debug { "Unknown robotGroupOpt ${tipsInfo.robotGroupOpt}, message=$message" }
}
else -> markNotConsumed()
}
return markAsConsumed()
}
/**

View File

@ -0,0 +1,45 @@
/*
* 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.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.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
val proto = buf.loadAs(TroopTips0x857.NotifyMsgBody.serializer(), 1)
val recallReminder = proto.optMsgRecall ?: return
val operator = group[recallReminder.uin] ?: return
markAsConsumed()
for (firstPkg in recallReminder.recalledMsgList) {
if (firstPkg.authorUin == bot.id && operator.id == bot.id) continue // already broadcast
val author = group[firstPkg.authorUin] ?: continue
collected += MessageRecallEvent.GroupRecall(
bot = bot,
authorId = firstPkg.authorUin,
messageIds = recallReminder.recalledMsgList.mapToIntArray { it.seq },
messageInternalIds = recallReminder.recalledMsgList.mapToIntArray { it.msgRandom },
messageTime = firstPkg.time,
operator = operator,
group = group,
author = author,
)
}
}
}

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
package net.mamoe.mirai.internal.network.notice.priv
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
@ -19,6 +19,7 @@ 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.notice.NewContactSupport
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.proto.FrdSysMsg
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
package net.mamoe.mirai.internal.network.notice.priv
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel

View File

@ -7,9 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network.notice
package net.mamoe.mirai.internal.network.notice.priv
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.getGroupByUin
@ -18,7 +17,9 @@ 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.SimpleNoticeProcessor
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.utils.assertUnreachable
import net.mamoe.mirai.utils.context
/**
@ -47,7 +48,12 @@ internal class PrivateMessageNoticeProcessor : SimpleNoticeProcessor<MsgComm.Msg
166, 167, // 单向好友
208, // friend ptt, maybe also support stranger
-> {
handlePrivateMessage(data, bot.getFriend(senderUin) ?: bot.getStranger(senderUin) ?: return)
handlePrivateMessage(
data,
bot.getFriend(senderUin)?.impl()
?: bot.getStranger(senderUin)?.impl()
?: return
)
}
141, // group temp
@ -63,9 +69,8 @@ internal class PrivateMessageNoticeProcessor : SimpleNoticeProcessor<MsgComm.Msg
private suspend fun PipelineContext.handlePrivateMessage(
data: MsgComm.Msg,
user: User,
user: AbstractUser,
) = data.context {
user.impl()
if (!user.messageSeq.updateIfDifferentWith(msgHead.msgSeq)) return
if (contentHead?.autoReply == 1) return
@ -80,15 +85,15 @@ internal class PrivateMessageNoticeProcessor : SimpleNoticeProcessor<MsgComm.Msg
is FriendImpl -> FriendMessageSyncEvent(user, chain, time)
is StrangerImpl -> StrangerMessageSyncEvent(user, chain, time)
is NormalMemberImpl -> GroupTempMessageSyncEvent(user, chain, time)
else -> null
is AnonymousMemberImpl -> assertUnreachable()
}
} else {
when (user) {
is FriendImpl -> FriendMessageEvent(user, chain, time)
is StrangerImpl -> StrangerMessageEvent(user, chain, time)
is NormalMemberImpl -> GroupTempMessageEvent(user, chain, time)
else -> null
is AnonymousMemberImpl -> assertUnreachable()
}
} ?: error("unreachable")
}
}
}

View File

@ -320,8 +320,8 @@ internal class TroopTips0x857 : ProtoBuf {
@Serializable
internal class TemplParam(
@ProtoNumber(1) @JvmField val name: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoNumber(2) @JvmField val value: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoNumber(1) @JvmField val name: String = "",
@ProtoNumber(2) @JvmField val value: String = "",
) : ProtoBuf
@Serializable

View File

@ -10,16 +10,13 @@
package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt
import kotlinx.io.core.readUShort
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.GroupNameChangeEvent
import net.mamoe.mirai.event.events.MemberCardChangeEvent
import net.mamoe.mirai.event.events.MessageRecallEvent
import net.mamoe.mirai.event.events.NudgeEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
@ -33,20 +30,16 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x122
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.io.ProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket
import net.mamoe.mirai.internal.utils.io.serialization.writeJceRequestPacket
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.encodeToString
import net.mamoe.mirai.utils.mapToIntArray
//0C 01 B1 89 BE 09 5E 3D 72 A6 00 01 73 68 FC 06 00 00 00 3C
@ -100,189 +93,24 @@ internal inline fun lambda732(crossinline block: ByteReadPacket.(GroupImpl, QQAn
}
}
private fun handleMuteMemberPacket(
bot: QQAndroidBot,
group: GroupImpl,
operator: NormalMember,
target: Long,
timeSeconds: Int,
): Packet? {
if (target == 0L) {
val new = timeSeconds != 0
if (group.settings.isMuteAllField == new) {
return null
}
group.settings.isMuteAllField = new
return GroupMuteAllEvent(!new, new, group, operator)
}
if (target == bot.id) {
return when {
group.botMuteRemaining == timeSeconds -> null
timeSeconds == 0 || timeSeconds == 0xFFFF_FFFF.toInt() -> {
group.botAsMember.checkIsMemberImpl()._muteTimestamp = 0
BotUnmuteEvent(operator)
}
else -> {
group.botAsMember.checkIsMemberImpl()._muteTimestamp =
currentTimeSeconds().toInt() + timeSeconds
BotMuteEvent(timeSeconds, operator)
}
}
}
val member = group[target] ?: return null
member.checkIsMemberImpl()
if (member.muteTimeRemaining == timeSeconds) {
return null
}
member._muteTimestamp = currentTimeSeconds().toInt() + timeSeconds
return if (timeSeconds == 0) MemberUnmuteEvent(member, operator)
else MemberMuteEvent(member, timeSeconds, operator)
}
internal object Transformers732 : Map<Int, Lambda732> by mapOf(
// mute
0x0c to lambda732 { group: GroupImpl, bot: QQAndroidBot ->
val operatorUin = readUInt().toLong()
if (operatorUin == bot.id) {
return@lambda732 emptySequence()
}
val operator = group[operatorUin] ?: return@lambda732 emptySequence()
readUInt().toLong() // time
val length = readUShort().toInt()
val packetList: MutableList<Packet> = mutableListOf()
repeat(length) {
val target = readUInt().toLong()
val timeSeconds = readUInt()
handleMuteMemberPacket(bot, group, operator, target, timeSeconds.toInt())?.let {
packetList.add(it)
}
}
return@lambda732 packetList.asSequence()
TODO("removed")
},
// anonymous
0x0e to lambda732 { group: GroupImpl, _: QQAndroidBot ->
// 匿名
val operator = group[readUInt().toLong()] ?: return@lambda732 emptySequence()
val new = readInt() == 0
if (group.settings.isAnonymousChatEnabledField == new) {
return@lambda732 emptySequence()
}
group.settings.isAnonymousChatEnabledField = new
return@lambda732 sequenceOf(GroupAllowAnonymousChatEvent(!new, new, group, operator))
TODO("removed")
},
//系统提示
0x14 to lambda732 { group: GroupImpl, bot: QQAndroidBot ->
discardExact(1)
val grayTip = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer()).optGeneralGrayTip
when (grayTip?.templId) {
//戳一戳
10043L, 1133L, 1132L, 1134L, 1135L, 1136L -> {
//预置数据,服务器将不会提供己方已知消息
var action = ""
var from: Member = group.botAsMember
var target: Member = group.botAsMember
var suffix = ""
grayTip.msgTemplParam.map {
Pair(it.name.decodeToString(), it.value.decodeToString())
}.asSequence().forEach { (key, value) ->
run {
when (key) {
"action_str" -> action = value
"uin_str1" -> from = group[value.toLong()] ?: return@lambda732 emptySequence()
"uin_str2" -> target = group[value.toLong()] ?: return@lambda732 emptySequence()
"suffix_str" -> suffix = value
}
}
}
return@lambda732 sequenceOf(
NudgeEvent(
from = if (from.id == bot.id) bot else from,
target = if (target.id == bot.id) bot else target,
action = action,
suffix = suffix,
subject = group,
),
)
}
//龙王
10093L, 1053L, 1054L -> {
var now: NormalMember = group.botAsMember
var previous: NormalMember? = null
grayTip.msgTemplParam.asSequence().map {
it.name.decodeToString() to it.value.decodeToString()
}.forEach { (key, value) ->
when (key) {
"uin" -> now = group[value.toLong()] ?: return@lambda732 emptySequence()
"uin_last" -> previous = group[value.toLong()] ?: return@lambda732 emptySequence()
}
}
return@lambda732 previous?.let {
sequenceOf(
GroupTalkativeChangeEvent(group, now, it),
MemberHonorChangeEvent.Lose(it, GroupHonorType.TALKATIVE),
MemberHonorChangeEvent.Achieve(now, GroupHonorType.TALKATIVE),
)
} ?: sequenceOf(MemberHonorChangeEvent.Achieve(now, GroupHonorType.TALKATIVE))
}
else -> {
bot.network.logger.debug {
"Unknown Transformers528 0x14 template\ntemplId=${grayTip?.templId}\nPermList=${grayTip?.msgTemplParam?._miraiContentToString()}"
}
return@lambda732 emptySequence()
}
}
TODO("removed")
},
// 传字符串信息
0x10 to lambda732 { group: GroupImpl, bot: QQAndroidBot ->
discardExact(1)
readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer()).let { body ->
when (body.optEnumType) {
1 -> body.optMsgGraytips?.let { tipsInfo ->
val message = tipsInfo.optBytesContent.decodeToString()
//机器人信息
if (tipsInfo.robotGroupOpt != 0) {
TODO("removed")
} else when {
message.endsWith("群聊坦白说") -> {
val new = when (message) {
"管理员已关闭群聊坦白说" -> false
"管理员已开启群聊坦白说" -> true
else -> {
bot.network.logger.debug { "Unknown server confess talk messages $message" }
return@lambda732 emptySequence()
}
}
return@lambda732 sequenceOf(
GroupAllowConfessTalkEvent(
new,
!new,
group,
false,
),
)
}
else -> {
bot.network.logger.debug { "Unknown server messages $message" }
return@lambda732 emptySequence()
}
}
}
else -> {
bot.network.logger.debug {
"Unknown Transformers732 0x10 optEnumType\noptEnumType=${body.optEnumType}\ncontent=${body._miraiContentToString()}"
}
return@lambda732 emptySequence()
}
} ?: return@lambda732 emptySequence()
}
TODO("removed")
/*
val dataBytes = readBytes(26)
@ -352,31 +180,7 @@ internal object Transformers732 : Map<Int, Lambda732> by mapOf(
// recall
0x11 to lambda732 { group: GroupImpl, bot: QQAndroidBot ->
discardExact(1)
val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer())
val recallReminder = proto.optMsgRecall ?: return@lambda732 emptySequence()
val operator =
if (recallReminder.uin == bot.id) group.botAsMember
else group[recallReminder.uin] ?: return@lambda732 emptySequence()
val firstPkg = recallReminder.recalledMsgList.firstOrNull() ?: return@lambda732 emptySequence()
return@lambda732 when {
firstPkg.authorUin == bot.id && operator.id == bot.id -> emptySequence()
else -> sequenceOf(
MessageRecallEvent.GroupRecall(
bot,
firstPkg.authorUin,
recallReminder.recalledMsgList.mapToIntArray { it.seq },
recallReminder.recalledMsgList.mapToIntArray { it.msgRandom },
firstPkg.time,
operator,
group,
group[firstPkg.authorUin] ?: return@lambda732 emptySequence(),
),
)
}
TODO("removed")
},
)