From 1e885dbf7a96ff58bf67e6a40e45b062e0778512 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Sat, 9 May 2020 16:11:29 +0800 Subject: [PATCH] Rearrange MessageSvc and OnlinePush --- .../mirai/qqandroid/contact/GroupImpl.kt | 6 +- .../mirai/qqandroid/contact/MemberImpl.kt | 6 +- .../net/mamoe/mirai/qqandroid/contact/util.kt | 6 +- .../qqandroid/message/outgoingSourceImpl.kt | 4 +- .../network/QQAndroidBotNetworkHandler.kt | 6 +- .../protocol/data/proto/msgType0x210.kt | 2 +- .../network/protocol/packet/PacketFactory.kt | 50 +- .../chat/receive/MessageSvc.PbDeleteMsg.kt | 55 ++ .../chat/receive/MessageSvc.PbGetMsg.kt | 346 +++++++++ .../chat/receive/MessageSvc.PbSendMsg.kt | 237 ++++++ .../receive/MessageSvc.PushForceOffline.kt | 29 + .../chat/receive/MessageSvc.PushNotify.kt | 42 ++ .../packet/chat/receive/MessageSvc.kt | 600 --------------- .../chat/receive/OnlinePush.PbPushGroupMsg.kt | 115 +++ .../chat/receive/OnlinePush.PbPushTransMsg.kt | 144 ++++ .../packet/chat/receive/OnlinePush.ReqPush.kt | 518 +++++++++++++ .../packet/chat/receive/OnlinePush.kt | 698 ------------------ .../net.mamoe.mirai/event/events/BotEvents.kt | 2 +- 18 files changed, 1526 insertions(+), 1340 deletions(-) create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt index 3de8f7418..2040aa7f7 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt @@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService import net.mamoe.mirai.qqandroid.utils.estimateLength import net.mamoe.mirai.utils.* @@ -358,7 +358,7 @@ internal class GroupImpl( lateinit var source: MessageSourceToGroupImpl bot.network.run { - val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.createToGroup( + val response: MessageSvcPbSendMsg.Response = MessageSvcPbSendMsg.createToGroup( bot.client, this@GroupImpl, msg, @@ -366,7 +366,7 @@ internal class GroupImpl( ) { source = it }.sendAndExpect() - if (response is MessageSvc.PbSendMsg.Response.Failed) { + if (response is MessageSvcPbSendMsg.Response.Failed) { when (response.resultType) { 120 -> throw BotIsBeingMutedException(this@GroupImpl) 34 -> { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt index 495bb45e1..467818cdf 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt @@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.utils.* import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -68,13 +68,13 @@ internal class MemberImpl constructor( lateinit var source: MessageSourceToTempImpl bot.network.run { check( - MessageSvc.PbSendMsg.createToTemp( + MessageSvcPbSendMsg.createToTemp( bot.client, this@MemberImpl, message.asMessageChain() ) { source = it - }.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS + }.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS ) { "send message failed" } } return MessageReceipt(source, this, null) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt index 83564cbca..65afec207 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt @@ -23,7 +23,7 @@ import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl import net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.verbose @@ -38,13 +38,13 @@ internal suspend fun <T : Contact> Friend.sendMessageImpl(generic: T, message: M lateinit var source: MessageSourceToFriendImpl (bot.network as QQAndroidBotNetworkHandler).run { check( - MessageSvc.PbSendMsg.createToFriend( + MessageSvcPbSendMsg.createToFriend( bot.asQQAndroidBot().client, this@sendMessageImpl, event.message ) { source = it - }.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS + }.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS ) { "send message failed" } } return MessageReceipt(source, generic, null) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt index b73f0479b..b26951e7b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt @@ -25,7 +25,7 @@ import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushPbPushGroupMsg.SendGroupMessageReceipt import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray @@ -117,7 +117,7 @@ internal class MessageSourceToGroupImpl( override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false) private val sequenceIdDeferred: Deferred<Int?> = - coroutineScope.asyncFromEventOrNull<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>( + coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, Int>( timeoutMillis = 3000 ) { if (it.messageRandom == this@MessageSourceToGroupImpl.internalId) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 515fd7869..cbd16b01c 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopNum import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbGetMsg import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat @@ -362,8 +362,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo logger.info { "Syncing friend message history..." } withTimeoutOrNull(30000) { - launch { syncFromEvent<MessageSvc.PbGetMsg.GetMsgSuccess, Unit> { Unit } } - MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<Packet>() + launch { syncFromEvent<MessageSvcPbGetMsg.GetMsgSuccess, Unit> { Unit } } + MessageSvcPbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<Packet>() } ?: error("timeout syncing friend message history") logger.info { "Syncing friend message history: Success" } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt index 208cc38f4..a5ecfbcea 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt @@ -897,7 +897,7 @@ internal class Submsgtype0x27 { ) : ProtoBuf @Serializable - internal class MsgBody( + internal class SubMsgType0x27MsgBody( @ProtoId(1) @JvmField val msgModInfos: List<ForwardBody> = listOf() ) : ProtoBuf diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index a1ae4a7b7..54be09f01 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -19,8 +19,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.* import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc @@ -132,10 +131,10 @@ internal object KnownPacketFactories { WtLogin.Login, StatSvc.Register, StatSvc.GetOnlineStatus, - MessageSvc.PbGetMsg, - MessageSvc.PushForceOffline, - MessageSvc.PbSendMsg, - MessageSvc.Del, + MessageSvcPbGetMsg, + MessageSvcPushForceOffline, + MessageSvcPbSendMsg, + MessageSvcPbDeleteMsg, FriendList.GetFriendGroupList, FriendList.GetTroopListSimplify, FriendList.GetTroopMemberList, @@ -157,15 +156,15 @@ internal object KnownPacketFactories { ) object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf( - OnlinePush.PbPushGroupMsg, - OnlinePush.ReqPush, - OnlinePush.PbPushTransMsg, - MessageSvc.PushNotify, + OnlinePushPbPushGroupMsg, + OnlinePushReqPush, + OnlinePushPbPushTransMsg, + MessageSvcPushNotify, ConfigPushSvc.PushReq, StatSvc.ReqMSFOffline ) // SvcReqMSFLoginNotify 自己的其他设备上限 - // MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机 + // MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机 // OnlinePush.PbC2CMsgSync 电脑发消息给别人, 同步给手机 @Suppress("MemberVisibilityCanBePrivate") // debugging use @@ -259,21 +258,20 @@ internal object KnownPacketFactories { PacketLogger.info { "Handle packet: ${it.commandName}" } it.data.withUse { when (flag2) { - 0, 1 -> - when (it.packetFactory) { - is OutgoingPacketFactory<*> -> consumer( - it.packetFactory as OutgoingPacketFactory<T>, - it.packetFactory.run { decode(bot, it.data) }, - it.packetFactory.commandName, - it.sequenceId - ) - is IncomingPacketFactory<*> -> consumer( - it.packetFactory as IncomingPacketFactory<T>, - it.packetFactory.run { decode(bot, it.data, it.sequenceId) }, - it.packetFactory.receivingCommandName, - it.sequenceId - ) - } + 0, 1 -> when (it.packetFactory) { + is OutgoingPacketFactory<*> -> consumer( + it.packetFactory as OutgoingPacketFactory<T>, + it.packetFactory.run { decode(bot, it.data) }, + it.packetFactory.commandName, + it.sequenceId + ) + is IncomingPacketFactory<*> -> consumer( + it.packetFactory as IncomingPacketFactory<T>, + it.packetFactory.run { decode(bot, it.data, it.sequenceId) }, + it.packetFactory.receivingCommandName, + it.sequenceId + ) + } 2 -> it.data.parseOicqResponse( bot, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt new file mode 100644 index 000000000..5bd263ec4 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf + +internal object MessageSvcPbDeleteMsg : OutgoingPacketFactory<Nothing?>("MessageSvcPbDeleteMsg") { + + internal operator fun invoke(client: QQAndroidClient, items: List<MsgSvc.PbDeleteMsgReq.MsgItem>) = + buildOutgoingUniPacket(client) { + + writeProtoBuf( + MsgSvc.PbDeleteMsgReq.serializer(), + MsgSvc.PbDeleteMsgReq( + msgItems = items + ) + ) + } + + internal suspend fun delete(bot: QQAndroidBot, messages: Flow<MsgComm.Msg>) = + bot.network.run { + + val map = messages.map { + MsgSvc.PbDeleteMsgReq.MsgItem( + fromUin = it.msgHead.fromUin, + toUin = it.msgHead.toUin, + // 群为84、好友为187。群通过其他方法删除,但测试结果显示通过187也能删除群消息。 + msgType = 187, + msgSeq = it.msgHead.msgSeq, + msgUid = it.msgHead.msgUid + ) + }.toList() + + MessageSvcPbDeleteMsg(bot.client, map).sendWithoutExpect() + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot) = null +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt new file mode 100644 index 000000000..b4ebdebfa --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt @@ -0,0 +1,346 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.atomicfu.loop +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.* +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.LowLevelAPI +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.data.MemberInfo +import net.mamoe.mirai.event.AbstractEvent +import net.mamoe.mirai.event.Event +import net.mamoe.mirai.event.events.BotJoinGroupEvent +import net.mamoe.mirai.event.events.MemberJoinEvent +import net.mamoe.mirai.getFriendOrNull +import net.mamoe.mirai.message.FriendMessageEvent +import net.mamoe.mirai.message.TempMessageEvent +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl +import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl +import net.mamoe.mirai.qqandroid.message.toMessageChain +import net.mamoe.mirai.qqandroid.network.MultiPacket +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact +import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList +import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray +import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.utils.* + + +/** + * 获取好友消息和消息记录 + */ +@OptIn(MiraiInternalAPI::class) +internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Response>("MessageSvcPbGetMsg") { + @Suppress("SpellCheckingInspection") + operator fun invoke( + client: QQAndroidClient, + syncFlag: MsgSvc.SyncFlag = MsgSvc.SyncFlag.START, + msgTime: Long //PbPushMsg.msg.msgHead.msgTime + ): OutgoingPacket = buildOutgoingUniPacket( + client + ) { + //println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}") + writeProtoBuf( + MsgSvc.PbGetMsgReq.serializer(), + MsgSvc.PbGetMsgReq( + msgReqType = 1, // from.ctype.toInt() + contextFlag = 1, + rambleFlag = 0, + latestRambleNumber = 20, + otherRambleNumber = 3, + onlineSyncFlag = 1, + whisperSessionId = 0, + syncFlag = syncFlag, + // serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY, + syncCookie = client.c2cMessageSync.syncCookie + ?: SyncCookie(time = msgTime).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it }, + // syncFlag = client.c2cMessageSync.syncFlag, + //msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf, + //pubaccountCookie = client.c2cMessageSync.pubAccountCookie + ) + ) + } + + @OptIn(MiraiInternalAPI::class) + open class GetMsgSuccess(delegate: List<Packet>) : Response(MsgSvc.SyncFlag.STOP, delegate), Event, + Packet.NoLog { + override fun toString(): String = "MessageSvcPbGetMsg.GetMsgSuccess(messages=<Iterable>))" + } + + /** + * 不要直接 expect 这个 class. 它可能还没同步完成 + */ + @MiraiInternalAPI + open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) : + AbstractEvent(), + MultiPacket<Packet>, + Iterable<Packet> by (delegate) { + + override fun toString(): String = + "MessageSvcPbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))" + } + + object EmptyResponse : GetMsgSuccess(emptyList()) + + private suspend fun MsgComm.Msg.getNewGroup(bot: QQAndroidBot): Group? { + val troopNum = bot.network.run { + FriendList.GetTroopListSimplify(bot.client) + .sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2) + }.groups.firstOrNull { it.groupUin == msgHead.fromUin } ?: return null + + @Suppress("DuplicatedCode") + return GroupImpl( + bot = bot, + coroutineContext = bot.coroutineContext, + id = Group.calculateGroupCodeByGroupUin(msgHead.fromUin), + groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { + this as GroupInfoImpl + + if (this.delegate.groupName == null) { + this.delegate.groupName = troopNum.groupName + } + + if (this.delegate.groupMemo == null) { + this.delegate.groupMemo = troopNum.groupMemo + } + + if (this.delegate.groupUin == null) { + this.delegate.groupUin = troopNum.groupUin + } + + this.delegate.groupCode = troopNum.groupCode + }, + members = bot._lowLevelQueryGroupMemberList( + troopNum.groupUin, + troopNum.groupCode, + troopNum.dwGroupOwnerUin + ) + ) + } + + @OptIn(LowLevelAPI::class) + private fun MsgComm.Msg.getNewMemberInfo(): MemberInfo { + return object : MemberInfo { + override val nameCard: String get() = "" + override val permission: MemberPermission get() = MemberPermission.MEMBER + override val specialTitle: String get() = "" + override val muteTimestamp: Int get() = 0 + override val uin: Long get() = msgHead.authUin + override val nick: String = msgHead.authNick.takeIf { it.isNotEmpty() } + ?: msgHead.fromNick + } + } + + @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class, FlowPreview::class, LowLevelAPI::class) + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + // 00 00 01 0F 08 00 12 00 1A 34 08 FF C1 C4 F1 05 10 FF C1 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 8A CA 91 D1 0C 48 9B A5 BD 9B 0A 58 DE 9D 99 F8 08 60 1D 68 FF C1 C4 F1 05 70 00 20 02 2A 9D 01 08 F3 C1 C4 F1 05 10 A2 FF 8C F0 03 18 01 22 8A 01 0A 2A 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 A6 01 20 0B 28 AE F9 01 30 F4 C1 C4 F1 05 38 A7 E3 D8 D4 84 80 80 80 01 B8 01 CD B5 01 12 08 08 01 10 00 18 00 20 00 1A 52 0A 50 0A 27 08 00 10 F4 C1 C4 F1 05 18 A7 E3 D8 D4 04 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 08 0A 06 0A 04 4E 4D 53 4C 12 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 12 04 4A 02 08 00 30 01 2A 15 08 97 A2 C1 F1 05 10 95 A6 F5 E5 0C 18 01 30 01 40 01 48 81 01 2A 10 08 D3 F7 B5 F1 05 10 DD F1 92 B7 07 18 01 30 01 38 00 42 00 48 00 + val resp = readProtoBuf(MsgSvc.PbGetMsgResp.serializer()) + + if (resp.result != 0) { + bot.network.logger + .warning { "MessageSvcPushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" } + return EmptyResponse + } + + bot.client.c2cMessageSync.syncCookie = resp.syncCookie + bot.client.c2cMessageSync.pubAccountCookie = resp.pubAccountCookie + bot.client.c2cMessageSync.msgCtrlBuf = resp.msgCtrlBuf + + if (resp.uinPairMsgs == null) { + return EmptyResponse + } + + val messages = resp.uinPairMsgs.asFlow() + .filterNot { it.msg == null } + .flatMapConcat { it.msg!!.asFlow() } + .also { + MessageSvcPbDeleteMsg.delete( + bot, + it) + } // 删除消息 + .mapNotNull<MsgComm.Msg, Packet> { msg -> + + when (msg.msgHead.msgType) { + 33 -> { // 邀请入群 + + val group = bot.getGroupByUinOrNull(msg.msgHead.fromUin) + if (msg.msgHead.authUin == bot.id) { + if (group != null) { + return@mapNotNull null + } + // 新群 + + val newGroup = msg.getNewGroup(bot) ?: return@mapNotNull null + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + bot.groups.delegate.addLast(newGroup) + return@mapNotNull BotJoinGroupEvent(newGroup) + } else { + group ?: return@mapNotNull null + + if (group.members.contains(msg.msgHead.authUin)) { + return@mapNotNull null + } + + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + return@mapNotNull MemberJoinEvent.Invite(group.newMember(msg.getNewMemberInfo()) + .also { group.members.delegate.addLast(it) }) + } + } + 34 -> { // 主动入群 + + // 27 0B 60 E7 01 44 71 47 90 03 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 36 42 35 35 46 45 32 45 35 36 43 45 45 44 30 38 30 35 31 41 35 42 37 36 39 35 34 45 30 46 43 43 36 36 45 44 43 46 45 43 42 39 33 41 41 44 32 32 + val group = bot.getGroupByUinOrNull(msg.msgHead.fromUin) + group ?: return@mapNotNull null + + if (group.members.contains(msg.msgHead.authUin)) { + return@mapNotNull null + } + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + return@mapNotNull MemberJoinEvent.Active(group.newMember(msg.getNewMemberInfo()) + .also { group.members.delegate.addLast(it) }) + } + 166 -> { + + if (msg.msgHead.fromUin == bot.id) { + loop@ while (true) { + val instance = bot.client.getFriendSeq() + if (instance < msg.msgHead.msgSeq) { + if (bot.client.setFriendSeq(instance, msg.msgHead.msgSeq)) { + break@loop + } + } else break@loop + } + return@mapNotNull null + } + val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null + friend.checkIsFriendImpl() + + if (!bot.firstLoginSucceed) { + return@mapNotNull null + } + + friend.lastMessageSequence.loop { instant -> + if (msg.msgHead.msgSeq > instant) { + if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { + return@mapNotNull FriendMessageEvent( + friend, + msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true), + msg.msgHead.msgTime + ) + } + } else return@mapNotNull null + } + } + 208 -> { + // friend ptt + return@mapNotNull null + } + 529 -> { + // 好友文件 + return@mapNotNull null + } + 141 -> { + val tmpHead = msg.msgHead.c2cTmpMsgHead ?: return@mapNotNull null + val member = bot.getGroupByUinOrNull(tmpHead.groupUin)?.getOrNull(msg.msgHead.fromUin) + ?: return@mapNotNull null + + member.checkIsMemberImpl() + + if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) { + return@mapNotNull null + } + + member.lastMessageSequence.loop { instant -> + if (msg.msgHead.msgSeq > instant) { + if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { + return@mapNotNull TempMessageEvent( + member, + msg.toMessageChain( + bot, + groupIdOrZero = 0, + onlineSource = true, + isTemp = true + ), + msg.msgHead.msgTime + ) + } + } else return@mapNotNull null + } + } + 84, 87 -> { // 请求入群验证 和 被要求入群 + bot.network.run { + NewContact.SystemMsgNewGroup(bot.client).sendWithoutExpect() + } + return@mapNotNull null + } + 187 -> { // 请求加好友验证 + bot.network.run { + NewContact.SystemMsgNewFriend(bot.client).sendWithoutExpect() + } + return@mapNotNull null + } + // 732: 27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58 + else -> { + bot.network.logger.debug { "unknown PbGetMsg type ${msg.msgHead.msgType}" } + return@mapNotNull null + } + } + } + + val list: List<Packet> = messages.toList() + if (resp.syncFlag == MsgSvc.SyncFlag.STOP) { + return GetMsgSuccess( + list) + } + return Response( + resp.syncFlag, + list) + } + + override suspend fun QQAndroidBot.handle(packet: Response) { + when (packet.syncFlagFromServer) { + MsgSvc.SyncFlag.STOP -> return + MsgSvc.SyncFlag.START -> { + network.run { + MessageSvcPbGetMsg( + client, + MsgSvc.SyncFlag.CONTINUE, + currentTimeSeconds).sendAndExpect<Packet>() + } + return + } + + MsgSvc.SyncFlag.CONTINUE -> { + network.run { + MessageSvcPbGetMsg( + client, + MsgSvc.SyncFlag.CONTINUE, + currentTimeSeconds).sendAndExpect<Packet>() + } + return + } + } + } +} + diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt new file mode 100644 index 000000000..cd1575829 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt @@ -0,0 +1,237 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.toByteArray +import net.mamoe.mirai.contact.Friend +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.Member +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.PttMessage +import net.mamoe.mirai.message.data.firstOrNull +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl +import net.mamoe.mirai.qqandroid.message.MessageSourceToGroupImpl +import net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl +import net.mamoe.mirai.qqandroid.message.toRichTextElems +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.* +import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray +import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.math.absoluteValue +import kotlin.random.Random + +internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.Response>("MessageSvcPbSendMsg") { + sealed class Response : Packet { + object SUCCESS : Response() { + override fun toString(): String = "MessageSvcPbSendMsg.Response.SUCCESS" + } + + /** + * 121: 被限制? 个别号才不能发 + */ + data class Failed(val resultType: Int, val errorCode: Int, val errorMessage: String) : Response() { + override fun toString(): String = + "MessageSvcPbSendMsg.Response.Failed(resultType=$resultType, errorCode=$errorCode, errorMessage=$errorMessage)" + } + } + + inline fun createToFriend( + client: QQAndroidClient, + qq: Friend, + message: MessageChain, + crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit + ): OutgoingPacket { + val rand = Random.nextInt().absoluteValue + val source = MessageSourceToFriendImpl( + internalId = rand, + sender = client.bot, + target = qq, + time = currentTimeSeconds.toInt(), + sequenceId = client.nextFriendSeq(), + originalMessage = message + ) + sourceCallback(source) + return createToFriend( + client, + qq.id, + message, + source) + } + + /** + * 发送好友消息 + */ + @Suppress("FunctionName") + private fun createToFriend( + client: QQAndroidClient, + toUin: Long, + message: MessageChain, + source: MessageSourceToFriendImpl + ): OutgoingPacket = buildOutgoingUniPacket(client) { + ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) + + ///return@buildOutgoingUniPacket + writeProtoBuf( + MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( + routingHead = MsgSvc.RoutingHead(c2c = MsgSvc.C2C(toUin = toUin)), + contentHead = MsgComm.ContentHead(pkgNum = 1), + msgBody = ImMsgBody.MsgBody( + richText = ImMsgBody.RichText( + elems = message.toRichTextElems(forGroup = false, withGeneralFlags = true) + ) + ), + msgSeq = source.sequenceId, + msgRand = source.internalId, + syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer()) + // msgVia = 1 + ) + ) + } + + + inline fun createToTemp( + client: QQAndroidClient, + member: Member, + message: MessageChain, + sourceCallback: (MessageSourceToTempImpl) -> Unit + ): OutgoingPacket { + val source = MessageSourceToTempImpl( + internalId = Random.nextInt().absoluteValue, + sender = client.bot, + target = member, + time = currentTimeSeconds.toInt(), + sequenceId = client.atomicNextMessageSequenceId(), + originalMessage = message + ) + sourceCallback(source) + return createToTemp( + client, + (member.group as GroupImpl).uin, + member.id, + message, + source) + } + + /** + * 发送临时消息 + */ + private fun createToTemp( + client: QQAndroidClient, + groupUin: Long, + toUin: Long, + message: MessageChain, + source: MessageSourceToTempImpl + ): OutgoingPacket = buildOutgoingUniPacket(client) { + writeProtoBuf( + MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( + routingHead = MsgSvc.RoutingHead( + grpTmp = MsgSvc.GrpTmp(groupUin, toUin) + ), + contentHead = MsgComm.ContentHead(pkgNum = 1), + msgBody = ImMsgBody.MsgBody( + richText = ImMsgBody.RichText( + elems = message.toRichTextElems(forGroup = false, withGeneralFlags = true) + ) + ), + msgSeq = source.sequenceId, + msgRand = source.internalId, + syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer()) + ) + ) + } + + + inline fun createToGroup( + client: QQAndroidClient, + group: Group, + message: MessageChain, + isForward: Boolean, + sourceCallback: (MessageSourceToGroupImpl) -> Unit + ): OutgoingPacket { + + val source = MessageSourceToGroupImpl( + group, + internalId = Random.nextInt().absoluteValue, + sender = client.bot, + target = group, + time = currentTimeSeconds.toInt(), + originalMessage = message//, + // sourceMessage = message + ) + sourceCallback(source) + return createToGroup( + client, + group.id, + message, + isForward, + source) + } + + /** + * 发送群消息 + */ + @Suppress("FunctionName") + private fun createToGroup( + client: QQAndroidClient, + groupCode: Long, + message: MessageChain, + isForward: Boolean, + source: MessageSourceToGroupImpl + ): OutgoingPacket = buildOutgoingUniPacket(client) { + ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) + + // DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString()) + + ///return@buildOutgoingUniPacket + writeProtoBuf( + MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( + routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupCode)), + contentHead = MsgComm.ContentHead(pkgNum = 1), + msgBody = ImMsgBody.MsgBody( + richText = ImMsgBody.RichText( + elems = message.toRichTextElems(forGroup = true, withGeneralFlags = true), + ptt = message.firstOrNull(PttMessage)?.run { + ImMsgBody.Ptt(fileName = fileName.toByteArray(), fileMd5 = md5) + } + ) + ), + msgSeq = client.atomicNextMessageSequenceId(), + msgRand = source.internalId, + syncCookie = EMPTY_BYTE_ARRAY, + msgVia = 1, + msgCtrl = if (isForward) MsgCtrl.MsgCtrl( + msgFlag = 4 + ) else null + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val response = readProtoBuf(MsgSvc.PbSendMsgResp.serializer()) + return if (response.result == 0) { + Response.SUCCESS + } else { + Response.Failed( + response.result, + response.errtype, + response.errmsg) + } + } +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt new file mode 100644 index 000000000..4443622cb --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.event.events.BotOfflineEvent +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushForceOffline +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket + + +/** + * 被挤下线 + */ +internal object MessageSvcPushForceOffline : + OutgoingPacketFactory<BotOfflineEvent.Force>("MessageSvcPushForceOffline") { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): BotOfflineEvent.Force { + val struct = this.readUniPacket(RequestPushForceOffline.serializer()) + return BotOfflineEvent.Force(bot, title = struct.title ?: "", message = struct.tips ?: "") + } +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt new file mode 100644 index 000000000..04a7eff54 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushNotify +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc +import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket +import net.mamoe.mirai.utils.currentTimeSeconds + + +/** + * 告知要刷新好友消息 + */ +internal object MessageSvcPushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvcPushNotify") { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify { + discardExact(4) // don't remove + return readUniPacket(RequestPushNotify.serializer()) + } + + override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? { + + network.run { + return MessageSvcPbGetMsg( + client, + MsgSvc.SyncFlag.START, + packet.stMsgInfo?.uMsgTime ?: currentTimeSeconds + ) + } + } +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt deleted file mode 100644 index 5337fba75..000000000 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright 2020 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/master/LICENSE - */ - -@file: OptIn(LowLevelAPI::class) -@file:Suppress("EXPERIMENTAL_API_USAGE") - -package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive - -import kotlinx.atomicfu.loop -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.* -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.discardExact -import kotlinx.io.core.toByteArray -import net.mamoe.mirai.LowLevelAPI -import net.mamoe.mirai.contact.Friend -import net.mamoe.mirai.contact.Group -import net.mamoe.mirai.contact.Member -import net.mamoe.mirai.contact.MemberPermission -import net.mamoe.mirai.data.MemberInfo -import net.mamoe.mirai.event.AbstractEvent -import net.mamoe.mirai.event.Event -import net.mamoe.mirai.event.events.BotJoinGroupEvent -import net.mamoe.mirai.event.events.BotOfflineEvent -import net.mamoe.mirai.event.events.MemberJoinEvent -import net.mamoe.mirai.getFriendOrNull -import net.mamoe.mirai.message.FriendMessageEvent -import net.mamoe.mirai.message.TempMessageEvent -import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.PttMessage -import net.mamoe.mirai.message.data.Voice -import net.mamoe.mirai.message.data.firstOrNull -import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.contact.GroupImpl -import net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl -import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl -import net.mamoe.mirai.qqandroid.message.* -import net.mamoe.mirai.qqandroid.network.MultiPacket -import net.mamoe.mirai.qqandroid.network.Packet -import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushForceOffline -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushNotify -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgCtrl.MsgCtrl -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie -import net.mamoe.mirai.qqandroid.network.protocol.packet.* -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact -import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList -import net.mamoe.mirai.qqandroid.utils._miraiContentToString -import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf -import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket -import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray -import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf -import net.mamoe.mirai.utils.* -import kotlin.collections.firstOrNull -import kotlin.math.absoluteValue -import kotlin.random.Random - -internal class MessageSvc { - /** - * 告知要刷新好友消息 - */ - internal object PushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvc.PushNotify") { - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify { - discardExact(4) // don't remove - return readUniPacket(RequestPushNotify.serializer()) - } - - override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? { - - network.run { - return PbGetMsg( - client, - MsgSvc.SyncFlag.START, - packet.stMsgInfo?.uMsgTime ?: currentTimeSeconds - ) - } - } - } - - - /** - * 获取好友消息和消息记录 - */ - @OptIn(MiraiInternalAPI::class) - internal object PbGetMsg : OutgoingPacketFactory<PbGetMsg.Response>("MessageSvc.PbGetMsg") { - @Suppress("SpellCheckingInspection") - operator fun invoke( - client: QQAndroidClient, - syncFlag: MsgSvc.SyncFlag = MsgSvc.SyncFlag.START, - msgTime: Long //PbPushMsg.msg.msgHead.msgTime - ): OutgoingPacket = buildOutgoingUniPacket( - client - ) { - //println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}") - writeProtoBuf( - MsgSvc.PbGetMsgReq.serializer(), - MsgSvc.PbGetMsgReq( - msgReqType = 1, // from.ctype.toInt() - contextFlag = 1, - rambleFlag = 0, - latestRambleNumber = 20, - otherRambleNumber = 3, - onlineSyncFlag = 1, - whisperSessionId = 0, - syncFlag = syncFlag, - // serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY, - syncCookie = client.c2cMessageSync.syncCookie - ?: SyncCookie(time = msgTime).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it }, - // syncFlag = client.c2cMessageSync.syncFlag, - //msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf, - //pubaccountCookie = client.c2cMessageSync.pubAccountCookie - ) - ) - } - - @OptIn(MiraiInternalAPI::class) - open class GetMsgSuccess(delegate: List<Packet>) : Response(MsgSvc.SyncFlag.STOP, delegate), Event, - Packet.NoLog { - override fun toString(): String = "MessageSvc.PbGetMsg.GetMsgSuccess(messages=<Iterable>))" - } - - /** - * 不要直接 expect 这个 class. 它可能还没同步完成 - */ - @MiraiInternalAPI - open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) : - AbstractEvent(), - MultiPacket<Packet>, - Iterable<Packet> by (delegate) { - - override fun toString(): String = - "MessageSvc.PbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))" - } - - object EmptyResponse : GetMsgSuccess(emptyList()) - - private suspend fun MsgComm.Msg.getNewGroup(bot: QQAndroidBot): Group? { - val troopNum = bot.network.run { - FriendList.GetTroopListSimplify(bot.client) - .sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2) - }.groups.firstOrNull { it.groupUin == msgHead.fromUin } ?: return null - - @Suppress("DuplicatedCode") - return GroupImpl( - bot = bot, - coroutineContext = bot.coroutineContext, - id = Group.calculateGroupCodeByGroupUin(msgHead.fromUin), - groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { - this as GroupInfoImpl - - if (this.delegate.groupName == null) { - this.delegate.groupName = troopNum.groupName - } - - if (this.delegate.groupMemo == null) { - this.delegate.groupMemo = troopNum.groupMemo - } - - if (this.delegate.groupUin == null) { - this.delegate.groupUin = troopNum.groupUin - } - - this.delegate.groupCode = troopNum.groupCode - }, - members = bot._lowLevelQueryGroupMemberList( - troopNum.groupUin, - troopNum.groupCode, - troopNum.dwGroupOwnerUin - ) - ) - } - - private fun MsgComm.Msg.getNewMemberInfo(): MemberInfo { - return object : MemberInfo { - override val nameCard: String get() = "" - override val permission: MemberPermission get() = MemberPermission.MEMBER - override val specialTitle: String get() = "" - override val muteTimestamp: Int get() = 0 - override val uin: Long get() = msgHead.authUin - override val nick: String = msgHead.authNick.takeIf { it.isNotEmpty() } - ?: msgHead.fromNick - } - } - - @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class, FlowPreview::class, LowLevelAPI::class) - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { - // 00 00 01 0F 08 00 12 00 1A 34 08 FF C1 C4 F1 05 10 FF C1 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 8A CA 91 D1 0C 48 9B A5 BD 9B 0A 58 DE 9D 99 F8 08 60 1D 68 FF C1 C4 F1 05 70 00 20 02 2A 9D 01 08 F3 C1 C4 F1 05 10 A2 FF 8C F0 03 18 01 22 8A 01 0A 2A 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 A6 01 20 0B 28 AE F9 01 30 F4 C1 C4 F1 05 38 A7 E3 D8 D4 84 80 80 80 01 B8 01 CD B5 01 12 08 08 01 10 00 18 00 20 00 1A 52 0A 50 0A 27 08 00 10 F4 C1 C4 F1 05 18 A7 E3 D8 D4 04 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 08 0A 06 0A 04 4E 4D 53 4C 12 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 12 04 4A 02 08 00 30 01 2A 15 08 97 A2 C1 F1 05 10 95 A6 F5 E5 0C 18 01 30 01 40 01 48 81 01 2A 10 08 D3 F7 B5 F1 05 10 DD F1 92 B7 07 18 01 30 01 38 00 42 00 48 00 - val resp = readProtoBuf(MsgSvc.PbGetMsgResp.serializer()) - - if (resp.result != 0) { - bot.network.logger - .warning { "MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" } - return EmptyResponse - } - - bot.client.c2cMessageSync.syncCookie = resp.syncCookie - bot.client.c2cMessageSync.pubAccountCookie = resp.pubAccountCookie - bot.client.c2cMessageSync.msgCtrlBuf = resp.msgCtrlBuf - - if (resp.uinPairMsgs == null) { - return EmptyResponse - } - - val messages = resp.uinPairMsgs.asFlow() - .filterNot { it.msg == null } - .flatMapConcat { it.msg!!.asFlow() } - .also { Del.delete(bot, it) } // 删除消息 - .mapNotNull<MsgComm.Msg, Packet> { msg -> - - when (msg.msgHead.msgType) { - 33 -> { // 邀请入群 - - val group = bot.getGroupByUinOrNull(msg.msgHead.fromUin) - if (msg.msgHead.authUin == bot.id) { - if (group != null) { - return@mapNotNull null - } - // 新群 - - val newGroup = msg.getNewGroup(bot) ?: return@mapNotNull null - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - bot.groups.delegate.addLast(newGroup) - return@mapNotNull BotJoinGroupEvent(newGroup) - } else { - group ?: return@mapNotNull null - - if (group.members.contains(msg.msgHead.authUin)) { - return@mapNotNull null - } - - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - return@mapNotNull MemberJoinEvent.Invite(group.newMember(msg.getNewMemberInfo()) - .also { group.members.delegate.addLast(it) }) - } - } - 34 -> { // 主动入群 - - // 27 0B 60 E7 01 44 71 47 90 03 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 36 42 35 35 46 45 32 45 35 36 43 45 45 44 30 38 30 35 31 41 35 42 37 36 39 35 34 45 30 46 43 43 36 36 45 44 43 46 45 43 42 39 33 41 41 44 32 32 - val group = bot.getGroupByUinOrNull(msg.msgHead.fromUin) - group ?: return@mapNotNull null - - if (group.members.contains(msg.msgHead.authUin)) { - return@mapNotNull null - } - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - return@mapNotNull MemberJoinEvent.Active(group.newMember(msg.getNewMemberInfo()) - .also { group.members.delegate.addLast(it) }) - } - 166 -> { - - if (msg.msgHead.fromUin == bot.id) { - loop@ while (true) { - val instance = bot.client.getFriendSeq() - if (instance < msg.msgHead.msgSeq) { - if (bot.client.setFriendSeq(instance, msg.msgHead.msgSeq)) { - break@loop - } - } else break@loop - } - return@mapNotNull null - } - val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null - friend.checkIsFriendImpl() - - if (!bot.firstLoginSucceed) { - return@mapNotNull null - } - - friend.lastMessageSequence.loop { instant -> - if (msg.msgHead.msgSeq > instant) { - if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { - return@mapNotNull FriendMessageEvent( - friend, - msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true), - msg.msgHead.msgTime - ) - } - } else return@mapNotNull null - } - } - 208 -> { - // friend ptt - return@mapNotNull null - } - 529 -> { - // 好友文件 - return@mapNotNull null - } - 141 -> { - val tmpHead = msg.msgHead.c2cTmpMsgHead ?: return@mapNotNull null - val member = bot.getGroupByUinOrNull(tmpHead.groupUin)?.getOrNull(msg.msgHead.fromUin) - ?: return@mapNotNull null - - member.checkIsMemberImpl() - - if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) { - return@mapNotNull null - } - - member.lastMessageSequence.loop { instant -> - if (msg.msgHead.msgSeq > instant) { - if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { - return@mapNotNull TempMessageEvent( - member, - msg.toMessageChain( - bot, - groupIdOrZero = 0, - onlineSource = true, - isTemp = true - ), - msg.msgHead.msgTime - ) - } - } else return@mapNotNull null - } - } - 84, 87 -> { // 请求入群验证 和 被要求入群 - bot.network.run { - NewContact.SystemMsgNewGroup(bot.client).sendWithoutExpect() - } - return@mapNotNull null - } - 187 -> { // 请求加好友验证 - bot.network.run { - NewContact.SystemMsgNewFriend(bot.client).sendWithoutExpect() - } - return@mapNotNull null - } - // 732: 27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58 - else -> { - bot.network.logger.debug { "unknown PbGetMsg type ${msg.msgHead.msgType}" } - return@mapNotNull null - } - } - } - - val list: List<Packet> = messages.toList() - if (resp.syncFlag == MsgSvc.SyncFlag.STOP) { - return GetMsgSuccess(list) - } - return Response(resp.syncFlag, list) - } - - override suspend fun QQAndroidBot.handle(packet: Response) { - when (packet.syncFlagFromServer) { - MsgSvc.SyncFlag.STOP -> return - MsgSvc.SyncFlag.START -> { - network.run { - PbGetMsg(client, MsgSvc.SyncFlag.CONTINUE, currentTimeSeconds).sendAndExpect<Packet>() - } - return - } - - MsgSvc.SyncFlag.CONTINUE -> { - network.run { - PbGetMsg(client, MsgSvc.SyncFlag.CONTINUE, currentTimeSeconds).sendAndExpect<Packet>() - } - return - } - } - } - } - - - /** - * 被挤下线 - */ - internal object PushForceOffline : OutgoingPacketFactory<BotOfflineEvent.Force>("MessageSvc.PushForceOffline") { - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): BotOfflineEvent.Force { - val struct = this.readUniPacket(RequestPushForceOffline.serializer()) - return BotOfflineEvent.Force(bot, title = struct.title ?: "", message = struct.tips ?: "") - } - } - - internal object PbSendMsg : OutgoingPacketFactory<PbSendMsg.Response>("MessageSvc.PbSendMsg") { - sealed class Response : Packet { - object SUCCESS : Response() { - override fun toString(): String = "MessageSvc.PbSendMsg.Response.SUCCESS" - } - - /** - * 121: 被限制? 个别号才不能发 - */ - data class Failed(val resultType: Int, val errorCode: Int, val errorMessage: String) : Response() { - override fun toString(): String = - "MessageSvc.PbSendMsg.Response.Failed(resultType=$resultType, errorCode=$errorCode, errorMessage=$errorMessage)" - } - } - - inline fun createToFriend( - client: QQAndroidClient, - qq: Friend, - message: MessageChain, - crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit - ): OutgoingPacket { - val rand = Random.nextInt().absoluteValue - val source = MessageSourceToFriendImpl( - internalId = rand, - sender = client.bot, - target = qq, - time = currentTimeSeconds.toInt(), - sequenceId = client.nextFriendSeq(), - originalMessage = message - ) - sourceCallback(source) - return createToFriend(client, qq.id, message, source) - } - - /** - * 发送好友消息 - */ - @Suppress("FunctionName") - private fun createToFriend( - client: QQAndroidClient, - toUin: Long, - message: MessageChain, - source: MessageSourceToFriendImpl - ): OutgoingPacket = buildOutgoingUniPacket(client) { - ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) - - ///return@buildOutgoingUniPacket - writeProtoBuf( - MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( - routingHead = MsgSvc.RoutingHead(c2c = MsgSvc.C2C(toUin = toUin)), - contentHead = MsgComm.ContentHead(pkgNum = 1), - msgBody = ImMsgBody.MsgBody( - richText = ImMsgBody.RichText( - elems = message.toRichTextElems(forGroup = false, withGeneralFlags = true) - ) - ), - msgSeq = source.sequenceId, - msgRand = source.internalId, - syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer()) - // msgVia = 1 - ) - ) - } - - - inline fun createToTemp( - client: QQAndroidClient, - member: Member, - message: MessageChain, - sourceCallback: (MessageSourceToTempImpl) -> Unit - ): OutgoingPacket { - val source = MessageSourceToTempImpl( - internalId = Random.nextInt().absoluteValue, - sender = client.bot, - target = member, - time = currentTimeSeconds.toInt(), - sequenceId = client.atomicNextMessageSequenceId(), - originalMessage = message - ) - sourceCallback(source) - return createToTemp(client, (member.group as GroupImpl).uin, member.id, message, source) - } - - /** - * 发送临时消息 - */ - private fun createToTemp( - client: QQAndroidClient, - groupUin: Long, - toUin: Long, - message: MessageChain, - source: MessageSourceToTempImpl - ): OutgoingPacket = buildOutgoingUniPacket(client) { - writeProtoBuf( - MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( - routingHead = MsgSvc.RoutingHead( - grpTmp = MsgSvc.GrpTmp(groupUin, toUin) - ), - contentHead = MsgComm.ContentHead(pkgNum = 1), - msgBody = ImMsgBody.MsgBody( - richText = ImMsgBody.RichText( - elems = message.toRichTextElems(forGroup = false, withGeneralFlags = true) - ) - ), - msgSeq = source.sequenceId, - msgRand = source.internalId, - syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer()) - ) - ) - } - - - inline fun createToGroup( - client: QQAndroidClient, - group: Group, - message: MessageChain, - isForward: Boolean, - sourceCallback: (MessageSourceToGroupImpl) -> Unit - ): OutgoingPacket { - - val source = MessageSourceToGroupImpl( - group, - internalId = Random.nextInt().absoluteValue, - sender = client.bot, - target = group, - time = currentTimeSeconds.toInt(), - originalMessage = message//, - // sourceMessage = message - ) - sourceCallback(source) - return createToGroup(client, group.id, message, isForward, source) - } - - /** - * 发送群消息 - */ - @Suppress("FunctionName") - private fun createToGroup( - client: QQAndroidClient, - groupCode: Long, - message: MessageChain, - isForward: Boolean, - source: MessageSourceToGroupImpl - ): OutgoingPacket = buildOutgoingUniPacket(client) { - ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) - - // DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString()) - - ///return@buildOutgoingUniPacket - writeProtoBuf( - MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( - routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupCode)), - contentHead = MsgComm.ContentHead(pkgNum = 1), - msgBody = ImMsgBody.MsgBody( - richText = ImMsgBody.RichText( - elems = message.toRichTextElems(forGroup = true, withGeneralFlags = true), - ptt = message.firstOrNull(PttMessage)?.run { - ImMsgBody.Ptt(fileName = fileName.toByteArray(), fileMd5 = md5) - } - ) - ), - msgSeq = client.atomicNextMessageSequenceId(), - msgRand = source.internalId, - syncCookie = EMPTY_BYTE_ARRAY, - msgVia = 1, - msgCtrl = if (isForward) MsgCtrl( - msgFlag = 4 - ) else null - ) - ) - } - - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { - val response = readProtoBuf(MsgSvc.PbSendMsgResp.serializer()) - return if (response.result == 0) { - Response.SUCCESS - } else { - Response.Failed(response.result, response.errtype, response.errmsg) - } - } - } - - internal object Del : OutgoingPacketFactory<Nothing?>("MessageSvc.PbDeleteMsg") { - - internal operator fun invoke(client: QQAndroidClient, items: List<MsgSvc.PbDeleteMsgReq.MsgItem>) = - buildOutgoingUniPacket(client) { - - writeProtoBuf( - MsgSvc.PbDeleteMsgReq.serializer(), - MsgSvc.PbDeleteMsgReq( - msgItems = items - ) - ) - } - - internal suspend fun delete(bot: QQAndroidBot, messages: Flow<MsgComm.Msg>) = - bot.network.run { - - val map = messages.map { - MsgSvc.PbDeleteMsgReq.MsgItem( - fromUin = it.msgHead.fromUin, - toUin = it.msgHead.toUin, - // 群为84、好友为187。群通过其他方法删除,但测试结果显示通过187也能删除群消息。 - msgType = 187, - msgSeq = it.msgHead.msgSeq, - msgUid = it.msgHead.msgUid - ) - }.toList() - - Del(bot.client, map).sendWithoutExpect() - } - - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot) = null - } -} - diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt new file mode 100644 index 000000000..1ee0f65d0 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.contact.nameCardOrNick +import net.mamoe.mirai.event.AbstractEvent +import net.mamoe.mirai.event.Event +import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.events.MemberCardChangeEvent +import net.mamoe.mirai.getGroupOrNull +import net.mamoe.mirai.message.GroupMessageEvent +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.MemberImpl +import net.mamoe.mirai.qqandroid.message.toMessageChain +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Oidb0x8fc +import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory +import net.mamoe.mirai.qqandroid.utils._miraiContentToString +import net.mamoe.mirai.qqandroid.utils.encodeToString +import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs +import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf + +/** + * 接受群消息 + */ +internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("OnlinePush.PbPushGroupMsg") { + internal class SendGroupMessageReceipt( + val messageRandom: Int, + val sequenceId: Int + ) : Packet, Event, Packet.NoLog, AbstractEvent() { + override fun toString(): String { + return "OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)" + } + } + + @OptIn(ExperimentalStdlibApi::class) + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { + // 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00 + if (!bot.firstLoginSucceed) return null + val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) + + if (pbPushMsg.msg.msgHead.fromUin == bot.id) { + return SendGroupMessageReceipt( + pbPushMsg.msg.msgBody.richText.attr!!.random, + pbPushMsg.msg.msgHead.msgSeq + ) + } + + var extraInfo: ImMsgBody.ExtraInfo? = null + var anonymous: ImMsgBody.AnonymousGroupMsg? = null + + for (elem in pbPushMsg.msg.msgBody.richText.elems) { + when { + elem.extraInfo != null -> extraInfo = elem.extraInfo + elem.anonGroupMsg != null -> anonymous = elem.anonGroupMsg + } + } + + val group = + bot.getGroupOrNull(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) as GroupImpl? ?: return null // 机器人还正在进群 + val sender = if (anonymous != null) { + group.newAnonymous(anonymous.anonNick.encodeToString()) + } else { + group[pbPushMsg.msg.msgHead.fromUin] + } as MemberImpl + + val name = if (anonymous != null) { + sender.nameCard + } else { + extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.run { + kotlin.runCatching { + if (this[0] == 0x0A.toByte()) + loadAs(Oidb0x8fc.CommCardNameBuf.serializer()).richCardName?.joinToString("") { it.text.encodeToString() } + else return@runCatching null + }.getOrNull() ?: encodeToString() + } ?: pbPushMsg.msg.msgHead.groupInfo.groupCard.takeIf { it.isNotEmpty() } + ?: sender.nameCardOrNick // 没有 extraInfo 就从 head 里取 + } + + val flags = extraInfo?.flags ?: 0 + return GroupMessageEvent( + senderName = name.also { + if (it != sender.nameCard) { + val origin = sender._nameCard + sender._nameCard = name + MemberCardChangeEvent(origin, name, sender).broadcast() + } + }, + sender = sender, + message = pbPushMsg.msg.toMessageChain(bot, groupIdOrZero = group.id, onlineSource = true), + permission = when { + flags and 16 != 0 -> MemberPermission.ADMINISTRATOR + flags and 8 != 0 -> MemberPermission.OWNER + flags == 0 -> MemberPermission.MEMBER + else -> { + bot.logger.warning("判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${pbPushMsg.msg.msgHead._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题.") + sender.permission + } + }, + time = pbPushMsg.msg.msgHead.msgTime + ) + } +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt new file mode 100644 index 000000000..a1a20bc4f --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:OptIn(MiraiInternalAPI::class, + MiraiExperimentalAPI::class, + JavaFriendlyAPI::class, + ExperimentalUnsignedTypes::class) + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import kotlinx.io.core.readUByte +import kotlinx.io.core.readUInt +import net.mamoe.mirai.JavaFriendlyAPI +import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent +import net.mamoe.mirai.event.events.MemberLeaveEvent +import net.mamoe.mirai.event.events.MemberPermissionChangeEvent +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.MemberImpl +import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OnlinePushTrans +import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket +import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.utils.read +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import net.mamoe.mirai.utils.MiraiInternalAPI + + +internal object OnlinePushPbPushTransMsg : + IncomingPacketFactory<Packet?>("OnlinePush.PbPushTransMsg", "OnlinePush.RespPush") { + + @OptIn(MiraiInternalAPI::class) + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { + val content = this.readProtoBuf(OnlinePushTrans.PbMsgInfo.serializer()) + + + if (!bot.client.pbPushTransMsgCacheList.ensureNoDuplication(content.msgSeq)) { + return null + } + + content.msgData.read<Unit> { + when (content.msgType) { + 44 -> { + this.discardExact(5) + val var4 = readByte().toInt() + var var5 = 0L + val target = readUInt().toLong() + if (var4 != 0 && var4 != 1) { + var5 = readUInt().toLong() + } + + val group = bot.getGroupByUin(content.fromUin) as GroupImpl + + if (var5 == 0L && this.remaining == 1L) {//管理员变更 + val newPermission = + if (this.readByte().toInt() == 1) MemberPermission.ADMINISTRATOR + else MemberPermission.MEMBER + + if (target == bot.id) { + if (group.botPermission == newPermission) { + return null + } + + return BotGroupPermissionChangeEvent( + group, + group.botPermission.also { + group.botAsMember.checkIsMemberImpl().permission = newPermission + }, + newPermission + ) + } else { + val member = group[target] as MemberImpl + if (member.permission == newPermission) { + return null + } + + return MemberPermissionChangeEvent( + member, + member.permission.also { member.permission = newPermission }, + newPermission + ) + } + } + } + 34 -> { + /* quit + 27 0B 60 E7 + 01 + 2F 55 7C B8 + 82 + 00 30 42 33 32 46 30 38 33 32 39 32 35 30 31 39 33 45 46 32 45 30 36 35 41 35 41 33 42 37 35 43 41 34 46 37 42 38 42 38 42 44 43 35 35 34 35 44 38 30 + */ + /* kick + 27 0B 60 E7 + 01 + A8 32 51 A1 + 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 39 32 46 45 30 36 31 41 33 37 36 43 44 35 37 35 37 39 45 37 32 34 44 37 37 30 36 46 39 39 43 35 35 33 33 31 34 44 32 44 46 35 45 42 43 31 31 36 + */ + readUInt().toLong() // group, uin or code ? + + discardExact(1) + val target = readUInt().toLong() + val type = readUByte().toInt() + val operator = readUInt().toLong() + val groupUin = content.fromUin + + when (type) { + 0x82 -> bot.getGroupByUinOrNull(groupUin)?.let { group -> + val member = group.getOrNull(target) as? MemberImpl ?: return null + return MemberLeaveEvent.Quit(member.also { + group.members.delegate.remove(member) + }) + } + 0x83 -> bot.getGroupByUin(groupUin).let { group -> + val member = group.getOrNull(target) as? MemberImpl ?: return null + return MemberLeaveEvent.Kick(member.also { + group.members.delegate.remove(member) + }, group.members[operator]) + } + } + } + } + } + return null + } + + override suspend fun QQAndroidBot.handle(packet: Packet?, sequenceId: Int): OutgoingPacket? { + return buildResponseUniPacket(client, sequenceId = sequenceId) {} + } + +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt new file mode 100644 index 000000000..26db88d6a --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt @@ -0,0 +1,518 @@ +/* + * Copyright 2020 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/master/LICENSE + */ + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:OptIn(MiraiInternalAPI::class, + MiraiExperimentalAPI::class, + JavaFriendlyAPI::class, + ExperimentalUnsignedTypes::class) + +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive + +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import kotlinx.io.core.readBytes +import kotlinx.io.core.readUInt +import kotlinx.serialization.Serializable +import net.mamoe.mirai.* +import net.mamoe.mirai.data.FriendInfo +import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl +import net.mamoe.mirai.qqandroid.contact.checkIsInstance +import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl +import net.mamoe.mirai.qqandroid.message.contextualBugReportException +import net.mamoe.mirai.qqandroid.network.MultiPacketBySequence +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgType0x210 +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.* +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0x44 +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0xb3 +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.TroopTips0x857 +import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.ignoredLambda528 +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.lambda528 +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.lambda732 +import net.mamoe.mirai.qqandroid.utils._miraiContentToString +import net.mamoe.mirai.qqandroid.utils.encodeToString +import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf +import net.mamoe.mirai.qqandroid.utils.io.readString +import net.mamoe.mirai.qqandroid.utils.io.serialization.* +import net.mamoe.mirai.qqandroid.utils.read +import net.mamoe.mirai.qqandroid.utils.toUHexString +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.currentTimeSeconds +import net.mamoe.mirai.utils.debug + + +//0C 01 B1 89 BE 09 5E 3D 72 A6 00 01 73 68 FC 06 00 00 00 3C +internal object OnlinePushReqPush : IncomingPacketFactory<OnlinePushReqPush.Response>( + "OnlinePush.ReqPush", + "OnlinePush.RespPush" +) { + // to reduce nesting depth + private fun List<MsgInfo>.deco( + client: QQAndroidClient, + mapper: ByteReadPacket.(msgInfo: MsgInfo) -> Sequence<Packet> + ): Sequence<Packet> { + return asSequence().filter { msg -> + client.onlinePushCacheList.ensureNoDuplication(msg.shMsgSeq) + }.flatMap { it.vMsg.read { mapper(it) } } + } + + @Suppress("unused") // bug + private fun lambda732(block: ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet>): + ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet> { + return block + } + + private fun lambda528(block: MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet>): + MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet> { + return block + } + + val ignoredLambda528: MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet> = lambda528 { emptySequence() } + + @ExperimentalUnsignedTypes + @OptIn(ExperimentalStdlibApi::class) + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Response { + val reqPushMsg = readUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req") + + val packets: Sequence<Packet> = reqPushMsg.vMsgInfos.deco(bot.client) { msgInfo -> + when (msgInfo.shMsgType.toInt()) { + 732 -> { + val group = bot.getGroup(readUInt().toLong()) + GroupImpl.checkIsInstance(group) + + val internalType = readByte().toInt() + discardExact(1) + + Transformers732[internalType] + ?.let { it(this@deco, group, bot) } + ?: kotlin.run { + bot.network.logger.debug { + "unknown group 732 type $internalType, data: " + readBytes().toUHexString() + } + return@deco emptySequence() + } + } + + // 00 27 1A 0C 1C 2C 3C 4C 5D 00 0C 6D 00 0C 7D 00 0C 8D 00 0C 9C AC BC CC DD 00 0C EC FC 0F 0B 2A 0C 1C 2C 3C 4C 5C 6C 0B 3A 0C 1C 2C 3C 4C 5C 6C 7C 8D 00 0C 9D 00 0C AC BD 00 0C CD 00 0C DC ED 00 0C FC 0F FC 10 0B 4A 0C 1C 2C 3C 4C 5C 6C 7C 8C 96 00 0B 5A 0C 1C 2C 3C 4C 5C 6C 7C 8C 9D 00 0C 0B 6A 0C 1A 0C 1C 26 00 0B 2A 0C 0B 3A 0C 16 00 0B 4A 09 0C 0B 5A 09 0C 0B 0B 7A 0C 1C 2C 36 00 0B 8A 0C 1C 2C 36 00 0B 9A 09 0C 0B AD 00 00 1E 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B + 528 -> { + val notifyMsgBody = readJceStruct(MsgType0x210.serializer()) + Transformers528[notifyMsgBody.uSubMsgType] + ?.let { processor -> processor(notifyMsgBody, bot) } + ?: kotlin.run { + bot.network.logger.debug { + // Network(1994701021) 16:03:54 : unknown group 528 type 0x0000000000000026, data: 08 01 12 40 0A 06 08 F4 EF BB 8F 04 10 E7 C1 AD B8 02 18 01 22 2C 10 01 1A 1A 18 B4 DC F8 9B 0C 20 E7 C1 AD B8 02 28 06 30 02 A2 01 04 08 93 D6 03 A8 01 08 20 00 28 00 32 08 18 01 20 FE AF AF F5 05 28 00 + // VIP 进群提示 + "unknown group 528 type 0x${notifyMsgBody.uSubMsgType.toUHexString("")}, data: " + notifyMsgBody.vProtobuf.toUHexString() + } + return@deco emptySequence() + } + } + else -> { + bot.network.logger.debug { "unknown sh type ${msgInfo.shMsgType.toInt()}" } + bot.network.logger.debug { "data=${readBytes().toUHexString()}" } + return@deco emptySequence() + } + } + } + return Response(reqPushMsg, packets) + } + + @Suppress("SpellCheckingInspection") + internal data class Response(val request: OnlinePushPack.SvcReqPushMsg, val sequence: Sequence<Packet>) : + MultiPacketBySequence<Packet>(sequence) { + override fun toString(): String { + return "OnlinePush.ReqPush.Response" + } + } + + override suspend fun QQAndroidBot.handle(packet: Response, sequenceId: Int): OutgoingPacket? { + return buildResponseUniPacket(client) { + writeJceStruct( + RequestPacket.serializer(), + RequestPacket( + sServantName = "OnlinePush", + sFuncName = "SvcRespPushMsg", + iRequestId = sequenceId, + sBuffer = jceRequestSBuffer( + "resp", + OnlinePushPack.SvcRespPushMsg.serializer(), + OnlinePushPack.SvcRespPushMsg( + packet.request.uin, + packet.request.vMsgInfos.map { msg -> + OnlinePushPack.DelMsgInfo( + fromUin = msg.lFromUin, + shMsgSeq = msg.shMsgSeq, + vMsgCookies = msg.vMsgCookies, + uMsgTime = msg.uMsgTime // captured 0 + ) + } + ) + ) + ) + ) + } + } +} + +private object Transformers732 : Map<Int, ByteReadPacket.(GroupImpl, QQAndroidBot) -> Sequence<Packet>> by mapOf( + // mute + 0x0c to lambda732 { group: GroupImpl, bot: QQAndroidBot -> + val operatorUin = readUInt().toLong() + if (operatorUin == bot.id) { + return@lambda732 emptySequence() + } + val operator = group.getOrNull(operatorUin) ?: return@lambda732 emptySequence() + readUInt().toLong() // time + this.discardExact(2) + val target = readUInt().toLong() + val timeSeconds = readInt() + + if (target == 0L) { + val new = timeSeconds != 0 + if (group.settings.isMuteAll == new) { + return@lambda732 emptySequence() + } + group._muteAll = new + return@lambda732 sequenceOf(GroupMuteAllEvent(!new, new, group, operator)) + } + + if (target == bot.id) { + return@lambda732 when { + group.botMuteRemaining == timeSeconds -> emptySequence() + timeSeconds == 0 || timeSeconds == 0xFFFF_FFFF.toInt() -> { + group.botAsMember.checkIsMemberImpl()._muteTimestamp = 0 + sequenceOf(BotUnmuteEvent(operator)) + } + else -> { + group.botAsMember.checkIsMemberImpl()._muteTimestamp = + currentTimeSeconds.toInt() + timeSeconds + sequenceOf(BotMuteEvent(timeSeconds, operator)) + } + } + } + + val member = group.getOrNull(target) ?: return@lambda732 emptySequence() + member.checkIsMemberImpl() + + if (member._muteTimestamp == timeSeconds) { + return@lambda732 emptySequence() + } + + member._muteTimestamp = timeSeconds + return@lambda732 if (timeSeconds == 0) sequenceOf(MemberUnmuteEvent(member, operator)) + else sequenceOf(MemberMuteEvent(member, timeSeconds, operator)) + }, + + // anonymous + 0x0e to lambda732 { group: GroupImpl, _: QQAndroidBot -> + // 匿名 + val operator = group.getOrNull(readUInt().toLong()) ?: return@lambda732 emptySequence() + val new = readInt() == 0 + if (group.settings.isAnonymousChatEnabled == new) { + return@lambda732 emptySequence() + } + + group._anonymousChat = new + return@lambda732 sequenceOf(GroupAllowAnonymousChatEvent(!new, new, group, operator)) + }, + + // 传字符串信息 + 0x10 to lambda732 { group: GroupImpl, bot: QQAndroidBot -> + val dataBytes = readBytes(26) + + when (dataBytes[0].toInt()) { + 59 -> { // TODO 应该在 Transformers528 处理 + val size = readByte().toInt() // orthodox, don't `readUByte` + if (size < 0) { + // java.lang.IllegalStateException: negative array size: -100, remaining bytes=B0 E6 99 90 D8 E8 02 98 06 01 + // java.lang.IllegalStateException: negative array size: -121, remaining bytes=03 10 D9 F7 A2 93 0D 18 E0 DB E8 CA 0B 32 22 61 34 64 31 34 64 61 64 65 65 38 32 32 34 62 64 32 35 34 65 63 37 62 62 30 33 30 66 61 36 66 61 6D 6A 38 0E 48 00 58 01 70 C8 E8 9B 07 7A AD 02 3C 7B 22 69 63 6F 6E 22 3A 22 71 71 77 61 6C 6C 65 74 5F 63 75 73 74 6F 6D 5F 74 69 70 73 5F 69 64 69 6F 6D 5F 69 63 6F 6E 2E 70 6E 67 22 2C 22 61 6C 74 22 3A 22 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 38 37 38 42 39 39 22 2C 22 74 65 78 74 22 3A 22 E6 8E A5 E9 BE 99 E7 BA A2 E5 8C 85 E4 B8 8B E4 B8 80 E4 B8 AA E6 8B BC E9 9F B3 EF BC 9A 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 45 36 32 35 35 35 22 2C 22 74 65 78 74 22 3A 22 64 69 6E 67 22 7D 3E 82 01 0C E8 80 81 E5 83 A7 E5 85 A5 E5 AE 9A 88 01 03 92 01 04 64 69 6E 67 A0 01 00 + // negative array size: -40, remaining bytes=D6 94 C3 8C D8 E8 02 98 06 01 + error("negative array size: $size, remaining bytes=${readBytes().toUHexString()}") + } + + // println(dataBytes.toUHexString()) + //println(message + ":" + dataBytes.toUHexString()) + + val new = when (val message = readString(size)) { + "管理员已关闭群聊坦白说" -> false + "管理员已开启群聊坦白说" -> true + else -> { + bot.network.logger.debug { "Unknown server messages $message" } + return@lambda732 emptySequence() + } + } + + if (group.settings.isConfessTalkEnabled == new) { + return@lambda732 emptySequence() + } + + return@lambda732 sequenceOf( + GroupAllowConfessTalkEvent( + new, + false, + group, + false + ) + ) + } + + 0x2D -> { + // 修改群名. 在 Transformers528 0x27L 处理 + return@lambda732 emptySequence() + } + else -> { + /* + bot.network.logger.debug("unknown Transformer732 0xunknown type: ${dataBytes[0].toString(16) + .toUpperCase()}") + bot.network.logger.debug("unknown Transformer732 0xdata= ${readBytes().toUHexString()}") + */ + return@lambda732 emptySequence() + + /* + if (group.name == message) { + return@lambda732 emptySequence() + } + + return@lambda732 sequenceOf( + GroupNameChangeEvent( + group.name.also { group._name = message }, + message, group, false + ) + )*/ + } + } + }, + + // 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.getOrNull(recallReminder.uin) ?: return@lambda732 emptySequence() + + return@lambda732 recallReminder.recalledMsgList.asSequence().mapNotNull { pkg -> + when { + pkg.authorUin == bot.id && operator.id == bot.id -> null + else -> { + MessageRecallEvent.GroupRecall( + bot, + pkg.authorUin, + pkg.seq, + pkg.msgRandom, + pkg.time, + operator, + group + ) + } + } + } + } +) + +// uSubMsgType to vProtobuf +// 138 or 139: top_package/akln.java:1568 +// 66: top_package/nhz.java:269 +/** + * @see MsgType0x210 + */ +@OptIn(LowLevelAPI::class, MiraiInternalAPI::class) +private object Transformers528 : Map<Long, MsgType0x210.(QQAndroidBot) -> Sequence<Packet>> by mapOf( + // 提示共同好友 + 0x111L to ignoredLambda528, + // 新好友 + 0xB3L to lambda528 { bot -> + // 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07 + val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer()) + val new = bot._lowLevelNewFriend(object : FriendInfo { + override val uin: Long get() = body.msgAddFrdNotify.fuin + override val nick: String get() = body.msgAddFrdNotify.fuinNick + }) + bot.friends.delegate.addLast(new) + return@lambda528 sequenceOf(FriendAddEvent(new)) + }, + 0xE2L to lambda528 { + // TODO: unknown. maybe messages. + // 0A 35 08 00 10 A2 FF 8C F0 03 1A 1B E5 90 8C E6 84 8F E4 BD A0 E7 9A 84 E5 8A A0 E5 A5 BD E5 8F 8B E8 AF B7 E6 B1 82 22 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B 28 01 + // vProtobuf.loadAs(Msgtype0x210.serializer()) + + return@lambda528 emptySequence() + }, + 0x44L to lambda528 { bot -> + val msg = vProtobuf.loadAs(Submsgtype0x44.Submsgtype0x44.MsgBody.serializer()) + when { + msg.msgCleanCountMsg != null -> { + + } + msg.msgFriendMsgSync != null -> { + + } + else -> { + bot.network.logger.debug { "OnlinePush528 0x44L: " + msg._miraiContentToString() } + } + } + return@lambda528 emptySequence() + }, + // bot 在其他客户端被踢或主动退出而同步情况 + 0xD4L to lambda528 { bot -> + @Serializable + data class SubD4( + // ok + val uin: Long + ) : ProtoBuf + + val uin = vProtobuf.loadAs(SubD4.serializer()).uin + val group = bot.getGroupByUinOrNull(uin) ?: bot.getGroupOrNull(uin) + return@lambda528 if (group != null && bot.groups.delegate.remove(group)) { + sequenceOf(BotLeaveEvent.Active(group)) + } else emptySequence() + }, + // 群相关, ModFriendRemark, DelFriend, ModGroupProfile + 0x27L to lambda528 { bot -> + fun ModFriendRemark.transform(bot: QQAndroidBot): Sequence<Packet> { + return this.msgFrdRmk?.asSequence()?.mapNotNull { + val friend = bot.getFriendOrNull(it.fuin) ?: return@mapNotNull null + // TODO: 2020/4/10 ADD REMARK QUERY + FriendRemarkChangeEvent(bot, friend, it.rmkName) + } ?: emptySequence() + } + + fun DelFriend.transform(bot: QQAndroidBot): Sequence<Packet> { + return this.uint64Uins?.asSequence()?.mapNotNull { + val friend = bot.getFriendOrNull(it) ?: return@mapNotNull null + if (bot.friends.delegate.remove(friend)) { + FriendDeleteEvent(friend) + } else null + } ?: emptySequence() + } + + fun ModGroupProfile.transform(bot: QQAndroidBot): Sequence<Packet> { + return this.msgGroupProfileInfos?.asSequence()?.mapNotNull { info -> + when (info.field) { + 1 -> { + // 群名 + val new = info.value.encodeToString() + + val group = bot.getGroupOrNull(this.groupCode) ?: return@mapNotNull null + group.checkIsGroupImpl() + val old = group.name + + if (new == old) return@mapNotNull null + + val operator = if (this.cmdUin == bot.id) null + else group.getOrNull(this.cmdUin) ?: return@mapNotNull null + + group._name = new + + return@mapNotNull GroupNameChangeEvent(old, new, group, operator) + } + 2 -> { + // 头像 + // top_package/akkz.java:3446 + /* + var4 = var82.byteAt(0); + short var3 = (short) (var82.byteAt(1) | var4 << 8); + var85 = var18.method_77927(var7 + ""); + var85.troopface = var3; + var85.hasSetNewTroopHead = true; + */ + bot.logger.debug(contextualBugReportException( + "解析 Transformers528 0x27L ModGroupProfile 群头像修改", + forDebug = "this=${this._miraiContentToString()}" + )) + null + } + 3 -> { // troop.credit.data + // top_package/akkz.java:3475 + // top_package/akkz.java:3498 + bot.logger.debug(contextualBugReportException( + "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data", + forDebug = "this=${this._miraiContentToString()}" + )) + null + } + + else -> null + } + } ?: emptySequence() + } + + fun ModGroupMemberProfile.transform(bot: QQAndroidBot): Sequence<Packet> { + return this.msgGroupMemberProfileInfos?.asSequence()?.mapNotNull { info -> + when (info.field) { + 1 -> { // name card + val new = info.value + val group = bot.getGroupOrNull(this.groupCode) ?: return@mapNotNull null + group.checkIsGroupImpl() + val member = group.getOrNull(this.uin) ?: return@mapNotNull null + member.checkIsMemberImpl() + + val old = member.nameCard + + if (new == old) return@mapNotNull null + member._nameCard = new + + return@mapNotNull MemberCardChangeEvent(old, new, member) + } + 2 -> { + if (info.value.singleOrNull()?.toInt() != 0) { + bot.logger.debug { + "Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}" + } + } + return@mapNotNull null + } + else -> { + bot.logger.debug { + "Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}" + } + return@mapNotNull null + } + } + } ?: emptySequence() + } + + fun ModCustomFace.transform(): Sequence<Packet> = + sequenceOf(BotFaceChangedEvent(Bot.getInstance(uin))) + + + return@lambda528 vProtobuf.loadAs(SubMsgType0x27MsgBody.serializer()).msgModInfos.asSequence() + .flatMap { + when { + it.msgModFriendRemark != null -> it.msgModFriendRemark.transform(bot) + it.msgDelFriend != null -> it.msgDelFriend.transform(bot) + it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot) + it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot) + it.msgModCustomFace != null -> it.msgModCustomFace.transform() + else -> { + bot.network.logger.debug { + "Transformers528 0x27L: new data: ${it._miraiContentToString()}" + } + emptySequence() + } + } + } + // 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B + } +) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt deleted file mode 100644 index e4caae7c5..000000000 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt +++ /dev/null @@ -1,698 +0,0 @@ -/* - * Copyright 2020 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/master/LICENSE - */ - -@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - -package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive - -import kotlinx.io.core.* -import kotlinx.serialization.Serializable -import net.mamoe.mirai.Bot -import net.mamoe.mirai.LowLevelAPI -import net.mamoe.mirai.contact.MemberPermission -import net.mamoe.mirai.contact.nameCardOrNick -import net.mamoe.mirai.data.FriendInfo -import net.mamoe.mirai.event.AbstractEvent -import net.mamoe.mirai.event.Event -import net.mamoe.mirai.event.broadcast -import net.mamoe.mirai.event.events.* -import net.mamoe.mirai.getFriendOrNull -import net.mamoe.mirai.getGroupOrNull -import net.mamoe.mirai.message.GroupMessageEvent -import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.contact.* -import net.mamoe.mirai.qqandroid.message.contextualBugReportException -import net.mamoe.mirai.qqandroid.message.toMessageChain -import net.mamoe.mirai.qqandroid.network.MultiPacketBySequence -import net.mamoe.mirai.qqandroid.network.Packet -import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgType0x210 -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.* -import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory -import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket -import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket -import net.mamoe.mirai.qqandroid.utils._miraiContentToString -import net.mamoe.mirai.qqandroid.utils.encodeToString -import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf -import net.mamoe.mirai.qqandroid.utils.io.readString -import net.mamoe.mirai.qqandroid.utils.io.serialization.* -import net.mamoe.mirai.qqandroid.utils.read -import net.mamoe.mirai.qqandroid.utils.toUHexString -import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.currentTimeSeconds -import net.mamoe.mirai.utils.debug - -internal class OnlinePush { - /** - * 接受群消息 - */ - internal object PbPushGroupMsg : IncomingPacketFactory<Packet?>("OnlinePush.PbPushGroupMsg") { - internal class SendGroupMessageReceipt( - val messageRandom: Int, - val sequenceId: Int - ) : Packet, Event, Packet.NoLog, AbstractEvent() { - override fun toString(): String { - return "OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)" - } - } - - @OptIn(ExperimentalStdlibApi::class) - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { - // 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00 - if (!bot.firstLoginSucceed) return null - val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) - - if (pbPushMsg.msg.msgHead.fromUin == bot.id) { - return SendGroupMessageReceipt( - pbPushMsg.msg.msgBody.richText.attr!!.random, - pbPushMsg.msg.msgHead.msgSeq - ) - } - - var extraInfo: ImMsgBody.ExtraInfo? = null - var anonymous: ImMsgBody.AnonymousGroupMsg? = null - - for (elem in pbPushMsg.msg.msgBody.richText.elems) { - when { - elem.extraInfo != null -> extraInfo = elem.extraInfo - elem.anonGroupMsg != null -> anonymous = elem.anonGroupMsg - } - } - - val group = - bot.getGroupOrNull(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) as GroupImpl? ?: return null // 机器人还正在进群 - val sender = if (anonymous != null) { - group.newAnonymous(anonymous.anonNick.encodeToString()) - } else { - group[pbPushMsg.msg.msgHead.fromUin] - } as MemberImpl - - val name = if (anonymous != null) { - sender.nameCard - } else { - extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.run { - kotlin.runCatching { - if (this[0] == 0x0A.toByte()) - loadAs(Oidb0x8fc.CommCardNameBuf.serializer()).richCardName?.joinToString("") { it.text.encodeToString() } - else return@runCatching null - }.getOrNull() ?: encodeToString() - } ?: pbPushMsg.msg.msgHead.groupInfo.groupCard.takeIf { it.isNotEmpty() } - ?: sender.nameCardOrNick // 没有 extraInfo 就从 head 里取 - } - - val flags = extraInfo?.flags ?: 0 - return GroupMessageEvent( - senderName = name.also { - if (it != sender.nameCard) { - val origin = sender._nameCard - sender._nameCard = name - MemberCardChangeEvent(origin, name, sender).broadcast() - } - }, - sender = sender, - message = pbPushMsg.msg.toMessageChain(bot, groupIdOrZero = group.id, onlineSource = true), - permission = when { - flags and 16 != 0 -> MemberPermission.ADMINISTRATOR - flags and 8 != 0 -> MemberPermission.OWNER - flags == 0 -> MemberPermission.MEMBER - else -> { - bot.logger.warning("判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${pbPushMsg.msg.msgHead._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题.") - sender.permission - } - }, - time = pbPushMsg.msg.msgHead.msgTime - ) - } - } - - internal object PbPushTransMsg : - IncomingPacketFactory<Packet?>("OnlinePush.PbPushTransMsg", "OnlinePush.RespPush") { - - @OptIn(MiraiInternalAPI::class) - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { - val content = this.readProtoBuf(OnlinePushTrans.PbMsgInfo.serializer()) - - - if (!bot.client.pbPushTransMsgCacheList.ensureNoDuplication(content.msgSeq)) { - return null - } - - content.msgData.read<Unit> { - when (content.msgType) { - 44 -> { - this.discardExact(5) - val var4 = readByte().toInt() - var var5 = 0L - val target = readUInt().toLong() - if (var4 != 0 && var4 != 1) { - var5 = readUInt().toLong() - } - - val group = bot.getGroupByUin(content.fromUin) as GroupImpl - - if (var5 == 0L && this.remaining == 1L) {//管理员变更 - val newPermission = - if (this.readByte().toInt() == 1) MemberPermission.ADMINISTRATOR - else MemberPermission.MEMBER - - if (target == bot.id) { - if (group.botPermission == newPermission) { - return null - } - - return BotGroupPermissionChangeEvent( - group, - group.botPermission.also { - group.botAsMember.checkIsMemberImpl().permission = newPermission - }, - newPermission - ) - } else { - val member = group[target] as MemberImpl - if (member.permission == newPermission) { - return null - } - - return MemberPermissionChangeEvent( - member, - member.permission.also { member.permission = newPermission }, - newPermission - ) - } - } - } - 34 -> { - /* quit - 27 0B 60 E7 - 01 - 2F 55 7C B8 - 82 - 00 30 42 33 32 46 30 38 33 32 39 32 35 30 31 39 33 45 46 32 45 30 36 35 41 35 41 33 42 37 35 43 41 34 46 37 42 38 42 38 42 44 43 35 35 34 35 44 38 30 - */ - /* kick - 27 0B 60 E7 - 01 - A8 32 51 A1 - 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 39 32 46 45 30 36 31 41 33 37 36 43 44 35 37 35 37 39 45 37 32 34 44 37 37 30 36 46 39 39 43 35 35 33 33 31 34 44 32 44 46 35 45 42 43 31 31 36 - */ - readUInt().toLong() // group, uin or code ? - - discardExact(1) - val target = readUInt().toLong() - val type = readUByte().toInt() - val operator = readUInt().toLong() - val groupUin = content.fromUin - - when (type) { - 0x82 -> bot.getGroupByUinOrNull(groupUin)?.let { group -> - val member = group.getOrNull(target) as? MemberImpl ?: return null - return MemberLeaveEvent.Quit(member.also { - group.members.delegate.remove(member) - }) - } - 0x83 -> bot.getGroupByUin(groupUin).let { group -> - val member = group.getOrNull(target) as? MemberImpl ?: return null - return MemberLeaveEvent.Kick(member.also { - group.members.delegate.remove(member) - }, group.members[operator]) - } - } - } - } - } - return null - } - - override suspend fun QQAndroidBot.handle(packet: Packet?, sequenceId: Int): OutgoingPacket? { - return buildResponseUniPacket(client, sequenceId = sequenceId) {} - } - - } - - //0C 01 B1 89 BE 09 5E 3D 72 A6 00 01 73 68 FC 06 00 00 00 3C - internal object ReqPush : IncomingPacketFactory<ReqPush.Response>( - "OnlinePush.ReqPush", - "OnlinePush.RespPush" - ) { - // to reduce nesting depth - private fun List<MsgInfo>.deco( - client: QQAndroidClient, - mapper: ByteReadPacket.(msgInfo: MsgInfo) -> Sequence<Packet> - ): Sequence<Packet> { - return asSequence().filter { msg -> - client.onlinePushCacheList.ensureNoDuplication(msg.shMsgSeq) - }.flatMap { it.vMsg.read { mapper(it) } } - } - - private fun lambda732(block: ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet>): - ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet> { - return block - } - - object Transformers732 : Map<Int, ByteReadPacket.(GroupImpl, QQAndroidBot) -> Sequence<Packet>> by mapOf( - // mute - 0x0c to lambda732 { group: GroupImpl, bot: QQAndroidBot -> - val operatorUin = readUInt().toLong() - if (operatorUin == bot.id) { - return@lambda732 emptySequence() - } - val operator = group.getOrNull(operatorUin) ?: return@lambda732 emptySequence() - readUInt().toLong() // time - this.discardExact(2) - val target = readUInt().toLong() - val timeSeconds = readInt() - - if (target == 0L) { - val new = timeSeconds != 0 - if (group.settings.isMuteAll == new) { - return@lambda732 emptySequence() - } - group._muteAll = new - return@lambda732 sequenceOf(GroupMuteAllEvent(!new, new, group, operator)) - } - - if (target == bot.id) { - return@lambda732 when { - group.botMuteRemaining == timeSeconds -> emptySequence() - timeSeconds == 0 || timeSeconds == 0xFFFF_FFFF.toInt() -> { - group.botAsMember.checkIsMemberImpl()._muteTimestamp = 0 - sequenceOf(BotUnmuteEvent(operator)) - } - else -> { - group.botAsMember.checkIsMemberImpl()._muteTimestamp = - currentTimeSeconds.toInt() + timeSeconds - sequenceOf(BotMuteEvent(timeSeconds, operator)) - } - } - } - - val member = group.getOrNull(target) ?: return@lambda732 emptySequence() - member.checkIsMemberImpl() - - if (member._muteTimestamp == timeSeconds) { - return@lambda732 emptySequence() - } - - member._muteTimestamp = timeSeconds - return@lambda732 if (timeSeconds == 0) sequenceOf(MemberUnmuteEvent(member, operator)) - else sequenceOf(MemberMuteEvent(member, timeSeconds, operator)) - }, - - // anonymous - 0x0e to lambda732 { group: GroupImpl, _: QQAndroidBot -> - // 匿名 - val operator = group.getOrNull(readUInt().toLong()) ?: return@lambda732 emptySequence() - val new = readInt() == 0 - if (group.settings.isAnonymousChatEnabled == new) { - return@lambda732 emptySequence() - } - - group._anonymousChat = new - return@lambda732 sequenceOf(GroupAllowAnonymousChatEvent(!new, new, group, operator)) - }, - - // 传字符串信息 - 0x10 to lambda732 { group: GroupImpl, bot: QQAndroidBot -> - val dataBytes = readBytes(26) - - when (dataBytes[0].toInt()) { - 59 -> { // TODO 应该在 Transformers528 处理 - val size = readByte().toInt() // orthodox, don't `readUByte` - if (size < 0) { - // java.lang.IllegalStateException: negative array size: -100, remaining bytes=B0 E6 99 90 D8 E8 02 98 06 01 - // java.lang.IllegalStateException: negative array size: -121, remaining bytes=03 10 D9 F7 A2 93 0D 18 E0 DB E8 CA 0B 32 22 61 34 64 31 34 64 61 64 65 65 38 32 32 34 62 64 32 35 34 65 63 37 62 62 30 33 30 66 61 36 66 61 6D 6A 38 0E 48 00 58 01 70 C8 E8 9B 07 7A AD 02 3C 7B 22 69 63 6F 6E 22 3A 22 71 71 77 61 6C 6C 65 74 5F 63 75 73 74 6F 6D 5F 74 69 70 73 5F 69 64 69 6F 6D 5F 69 63 6F 6E 2E 70 6E 67 22 2C 22 61 6C 74 22 3A 22 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 38 37 38 42 39 39 22 2C 22 74 65 78 74 22 3A 22 E6 8E A5 E9 BE 99 E7 BA A2 E5 8C 85 E4 B8 8B E4 B8 80 E4 B8 AA E6 8B BC E9 9F B3 EF BC 9A 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 45 36 32 35 35 35 22 2C 22 74 65 78 74 22 3A 22 64 69 6E 67 22 7D 3E 82 01 0C E8 80 81 E5 83 A7 E5 85 A5 E5 AE 9A 88 01 03 92 01 04 64 69 6E 67 A0 01 00 - // negative array size: -40, remaining bytes=D6 94 C3 8C D8 E8 02 98 06 01 - error("negative array size: $size, remaining bytes=${readBytes().toUHexString()}") - } - - // println(dataBytes.toUHexString()) - //println(message + ":" + dataBytes.toUHexString()) - - val new = when (val message = readString(size)) { - "管理员已关闭群聊坦白说" -> false - "管理员已开启群聊坦白说" -> true - else -> { - bot.network.logger.debug { "Unknown server messages $message" } - return@lambda732 emptySequence() - } - } - - if (group.settings.isConfessTalkEnabled == new) { - return@lambda732 emptySequence() - } - - return@lambda732 sequenceOf( - GroupAllowConfessTalkEvent( - new, - false, - group, - false - ) - ) - } - - 0x2D -> { - // 修改群名. 在 Transformers528 0x27L 处理 - return@lambda732 emptySequence() - } - else -> { - /* - bot.network.logger.debug("unknown Transformer732 0xunknown type: ${dataBytes[0].toString(16) - .toUpperCase()}") - bot.network.logger.debug("unknown Transformer732 0xdata= ${readBytes().toUHexString()}") - */ - return@lambda732 emptySequence() - - /* - if (group.name == message) { - return@lambda732 emptySequence() - } - - return@lambda732 sequenceOf( - GroupNameChangeEvent( - group.name.also { group._name = message }, - message, group, false - ) - )*/ - } - } - }, - - // 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.getOrNull(recallReminder.uin) ?: return@lambda732 emptySequence() - - return@lambda732 recallReminder.recalledMsgList.asSequence().mapNotNull { pkg -> - when { - pkg.authorUin == bot.id && operator.id == bot.id -> null - else -> { - MessageRecallEvent.GroupRecall( - bot, - pkg.authorUin, - pkg.seq, - pkg.msgRandom, - pkg.time, - operator, - group - ) - } - } - } - } - ) - - private fun lambda528(block: MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet>): - MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet> { - return block - } - - val ignoredLambda528: MsgType0x210.(bot: QQAndroidBot) -> Sequence<Packet> = lambda528 { emptySequence() } - - // uSubMsgType to vProtobuf - // 138 or 139: top_package/akln.java:1568 - // 66: top_package/nhz.java:269 - /** - * @see MsgType0x210 - */ - @OptIn(LowLevelAPI::class, MiraiInternalAPI::class) - object Transformers528 : Map<Long, MsgType0x210.(QQAndroidBot) -> Sequence<Packet>> by mapOf( - // 提示共同好友 - 0x111L to ignoredLambda528, - // 新好友 - 0xB3L to lambda528 { bot -> - // 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07 - val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer()) - val new = bot._lowLevelNewFriend(object : FriendInfo { - override val uin: Long get() = body.msgAddFrdNotify.fuin - override val nick: String get() = body.msgAddFrdNotify.fuinNick - }) - bot.friends.delegate.addLast(new) - return@lambda528 sequenceOf(FriendAddEvent(new)) - }, - 0xE2L to lambda528 { - // TODO: unknown. maybe messages. - // 0A 35 08 00 10 A2 FF 8C F0 03 1A 1B E5 90 8C E6 84 8F E4 BD A0 E7 9A 84 E5 8A A0 E5 A5 BD E5 8F 8B E8 AF B7 E6 B1 82 22 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B 28 01 - // vProtobuf.loadAs(Msgtype0x210.serializer()) - - return@lambda528 emptySequence() - }, - 0x44L to lambda528 { bot -> - val msg = vProtobuf.loadAs(Submsgtype0x44.Submsgtype0x44.MsgBody.serializer()) - when { - msg.msgCleanCountMsg != null -> { - - } - msg.msgFriendMsgSync != null -> { - - } - else -> { - bot.network.logger.debug { "OnlinePush528 0x44L: " + msg._miraiContentToString() } - } - } - return@lambda528 emptySequence() - }, - // bot 在其他客户端被踢或主动退出而同步情况 - 0xD4L to lambda528 { bot -> - @Serializable - data class SubD4( - // ok - val uin: Long - ) : ProtoBuf - - val uin = vProtobuf.loadAs(SubD4.serializer()).uin - val group = bot.getGroupByUinOrNull(uin) ?: bot.getGroupOrNull(uin) - return@lambda528 if (group != null && bot.groups.delegate.remove(group)) { - sequenceOf(BotLeaveEvent.Active(group)) - } else emptySequence() - }, - // 群相关, ModFriendRemark, DelFriend, ModGroupProfile - 0x27L to lambda528 { bot -> - fun Submsgtype0x27.SubMsgType0x27.ModFriendRemark.transform(bot: QQAndroidBot): Sequence<Packet> { - return this.msgFrdRmk?.asSequence()?.mapNotNull { - val friend = bot.getFriendOrNull(it.fuin) ?: return@mapNotNull null - // TODO: 2020/4/10 ADD REMARK QUERY - FriendRemarkChangeEvent(bot, friend, it.rmkName) - } ?: emptySequence() - } - - fun Submsgtype0x27.SubMsgType0x27.DelFriend.transform(bot: QQAndroidBot): Sequence<Packet> { - return this.uint64Uins?.asSequence()?.mapNotNull { - val friend = bot.getFriendOrNull(it) ?: return@mapNotNull null - if (bot.friends.delegate.remove(friend)) { - FriendDeleteEvent(friend) - } else null - } ?: emptySequence() - } - - fun Submsgtype0x27.SubMsgType0x27.ModGroupProfile.transform(bot: QQAndroidBot): Sequence<Packet> { - return this.msgGroupProfileInfos?.asSequence()?.mapNotNull { info -> - when (info.field) { - 1 -> { - // 群名 - val new = info.value.encodeToString() - - val group = bot.getGroupOrNull(this.groupCode) ?: return@mapNotNull null - group.checkIsGroupImpl() - val old = group.name - - if (new == old) return@mapNotNull null - - val operator = if (this.cmdUin == bot.id) null - else group.getOrNull(this.cmdUin) ?: return@mapNotNull null - - group._name = new - - return@mapNotNull GroupNameChangeEvent(old, new, group, operator) - } - 2 -> { - // 头像 - // top_package/akkz.java:3446 - /* - var4 = var82.byteAt(0); - short var3 = (short) (var82.byteAt(1) | var4 << 8); - var85 = var18.method_77927(var7 + ""); - var85.troopface = var3; - var85.hasSetNewTroopHead = true; - */ - bot.logger.debug(contextualBugReportException( - "解析 Transformers528 0x27L ModGroupProfile 群头像修改", - forDebug = "this=${this._miraiContentToString()}" - )) - null - } - 3 -> { // troop.credit.data - // top_package/akkz.java:3475 - // top_package/akkz.java:3498 - bot.logger.debug(contextualBugReportException( - "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data", - forDebug = "this=${this._miraiContentToString()}" - )) - null - } - - else -> null - } - } ?: emptySequence() - } - - fun Submsgtype0x27.SubMsgType0x27.ModGroupMemberProfile.transform(bot: QQAndroidBot): Sequence<Packet> { - return this.msgGroupMemberProfileInfos?.asSequence()?.mapNotNull { info -> - when (info.field) { - 1 -> { // name card - val new = info.value - val group = bot.getGroupOrNull(this.groupCode) ?: return@mapNotNull null - group.checkIsGroupImpl() - val member = group.getOrNull(this.uin) ?: return@mapNotNull null - member.checkIsMemberImpl() - - val old = member.nameCard - - if (new == old) return@mapNotNull null - member._nameCard = new - - return@mapNotNull MemberCardChangeEvent(old, new, member) - } - 2 -> { - if (info.value.singleOrNull()?.toInt() != 0) { - bot.logger.debug { - "Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}" - } - } - return@mapNotNull null - } - else -> { - bot.logger.debug { - "Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}" - } - return@mapNotNull null - } - } - } ?: emptySequence() - } - - fun Submsgtype0x27.SubMsgType0x27.ModCustomFace.transform(bot: QQAndroidBot): Sequence<Packet> = - sequenceOf(BotFaceChangedEvent(Bot.getInstance(uin))) - - - return@lambda528 vProtobuf.loadAs(Submsgtype0x27.SubMsgType0x27.MsgBody.serializer()).msgModInfos.asSequence() - .flatMap { - when { - it.msgModFriendRemark != null -> it.msgModFriendRemark.transform(bot) - it.msgDelFriend != null -> it.msgDelFriend.transform(bot) - it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot) - it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot) - it.msgModCustomFace != null -> it.msgModCustomFace.transform(bot) - else -> { - bot.network.logger.debug { - "Transformers528 0x27L: new data: ${it._miraiContentToString()}" - } - emptySequence() - } - } - } - // 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B - } - ) - - @ExperimentalUnsignedTypes - @OptIn(ExperimentalStdlibApi::class) - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Response { - val reqPushMsg = readUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req") - - val packets: Sequence<Packet> = reqPushMsg.vMsgInfos.deco(bot.client) { msgInfo -> - when (msgInfo.shMsgType.toInt()) { - 732 -> { - val group = bot.getGroup(readUInt().toLong()) - GroupImpl.checkIsInstance(group) - - val internalType = readByte().toInt() - discardExact(1) - - Transformers732[internalType] - ?.let { it(this@deco, group, bot) } - ?: kotlin.run { - bot.network.logger.debug { - "unknown group 732 type $internalType, data: " + readBytes().toUHexString() - } - return@deco emptySequence() - } - } - - // 00 27 1A 0C 1C 2C 3C 4C 5D 00 0C 6D 00 0C 7D 00 0C 8D 00 0C 9C AC BC CC DD 00 0C EC FC 0F 0B 2A 0C 1C 2C 3C 4C 5C 6C 0B 3A 0C 1C 2C 3C 4C 5C 6C 7C 8D 00 0C 9D 00 0C AC BD 00 0C CD 00 0C DC ED 00 0C FC 0F FC 10 0B 4A 0C 1C 2C 3C 4C 5C 6C 7C 8C 96 00 0B 5A 0C 1C 2C 3C 4C 5C 6C 7C 8C 9D 00 0C 0B 6A 0C 1A 0C 1C 26 00 0B 2A 0C 0B 3A 0C 16 00 0B 4A 09 0C 0B 5A 09 0C 0B 0B 7A 0C 1C 2C 36 00 0B 8A 0C 1C 2C 36 00 0B 9A 09 0C 0B AD 00 00 1E 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B - 528 -> { - val notifyMsgBody = readJceStruct(MsgType0x210.serializer()) - Transformers528[notifyMsgBody.uSubMsgType] - ?.let { processor -> processor(notifyMsgBody, bot) } - ?: kotlin.run { - bot.network.logger.debug { - // Network(1994701021) 16:03:54 : unknown group 528 type 0x0000000000000026, data: 08 01 12 40 0A 06 08 F4 EF BB 8F 04 10 E7 C1 AD B8 02 18 01 22 2C 10 01 1A 1A 18 B4 DC F8 9B 0C 20 E7 C1 AD B8 02 28 06 30 02 A2 01 04 08 93 D6 03 A8 01 08 20 00 28 00 32 08 18 01 20 FE AF AF F5 05 28 00 - // VIP 进群提示 - "unknown group 528 type 0x${notifyMsgBody.uSubMsgType.toUHexString("")}, data: " + notifyMsgBody.vProtobuf.toUHexString() - } - return@deco emptySequence() - } - } - else -> { - bot.network.logger.debug { "unknown sh type ${msgInfo.shMsgType.toInt()}" } - bot.network.logger.debug { "data=${readBytes().toUHexString()}" } - return@deco emptySequence() - } - } - } - return Response(reqPushMsg, packets) - } - - @Suppress("SpellCheckingInspection") - internal data class Response(val request: OnlinePushPack.SvcReqPushMsg, val sequence: Sequence<Packet>) : - MultiPacketBySequence<Packet>(sequence) { - override fun toString(): String { - return "OnlinePush.ReqPush.Response" - } - } - - override suspend fun QQAndroidBot.handle(packet: Response, sequenceId: Int): OutgoingPacket? { - return buildResponseUniPacket(client) { - writeJceStruct( - RequestPacket.serializer(), - RequestPacket( - sServantName = "OnlinePush", - sFuncName = "SvcRespPushMsg", - iRequestId = sequenceId, - sBuffer = jceRequestSBuffer( - "resp", - OnlinePushPack.SvcRespPushMsg.serializer(), - OnlinePushPack.SvcRespPushMsg( - packet.request.uin, - packet.request.vMsgInfos.map { msg -> - OnlinePushPack.DelMsgInfo( - fromUin = msg.lFromUin, - shMsgSeq = msg.shMsgSeq, - vMsgCookies = msg.vMsgCookies, - uMsgTime = msg.uMsgTime // captured 0 - ) - } - ) - ) - ) - ) - } - } - } -} - diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt index e41107f9a..cbac2aa5b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt @@ -155,7 +155,7 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() { override val messageInternalId: Int, override val messageTime: Int, /** - * 撤回操作人, 可能为 [Bot.uin] 或好友的 [User.id] + * 撤回操作人, 可能为 [Bot.id] 或好友的 [User.id] */ val operator: Long ) : MessageRecallEvent(), Packet {