mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-22 00:54:43 +08:00
Rearrange MessageSvc and OnlinePush
This commit is contained in:
parent
1c1a37a103
commit
1e885dbf7a
@ -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.highway.HighwayHelper
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
|
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.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.network.protocol.packet.list.ProfileService
|
||||||
import net.mamoe.mirai.qqandroid.utils.estimateLength
|
import net.mamoe.mirai.qqandroid.utils.estimateLength
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
@ -358,7 +358,7 @@ internal class GroupImpl(
|
|||||||
|
|
||||||
lateinit var source: MessageSourceToGroupImpl
|
lateinit var source: MessageSourceToGroupImpl
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.createToGroup(
|
val response: MessageSvcPbSendMsg.Response = MessageSvcPbSendMsg.createToGroup(
|
||||||
bot.client,
|
bot.client,
|
||||||
this@GroupImpl,
|
this@GroupImpl,
|
||||||
msg,
|
msg,
|
||||||
@ -366,7 +366,7 @@ internal class GroupImpl(
|
|||||||
) {
|
) {
|
||||||
source = it
|
source = it
|
||||||
}.sendAndExpect()
|
}.sendAndExpect()
|
||||||
if (response is MessageSvc.PbSendMsg.Response.Failed) {
|
if (response is MessageSvcPbSendMsg.Response.Failed) {
|
||||||
when (response.resultType) {
|
when (response.resultType) {
|
||||||
120 -> throw BotIsBeingMutedException(this@GroupImpl)
|
120 -> throw BotIsBeingMutedException(this@GroupImpl)
|
||||||
34 -> {
|
34 -> {
|
||||||
|
@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
|
|||||||
import net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl
|
import net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
|
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.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 net.mamoe.mirai.utils.*
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
@ -68,13 +68,13 @@ internal class MemberImpl constructor(
|
|||||||
lateinit var source: MessageSourceToTempImpl
|
lateinit var source: MessageSourceToTempImpl
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
check(
|
check(
|
||||||
MessageSvc.PbSendMsg.createToTemp(
|
MessageSvcPbSendMsg.createToTemp(
|
||||||
bot.client,
|
bot.client,
|
||||||
this@MemberImpl,
|
this@MemberImpl,
|
||||||
message.asMessageChain()
|
message.asMessageChain()
|
||||||
) {
|
) {
|
||||||
source = it
|
source = it
|
||||||
}.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
}.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS
|
||||||
) { "send message failed" }
|
) { "send message failed" }
|
||||||
}
|
}
|
||||||
return MessageReceipt(source, this, null)
|
return MessageReceipt(source, this, null)
|
||||||
|
@ -23,7 +23,7 @@ import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl
|
|||||||
import net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable
|
import net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable
|
||||||
import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
|
import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
|
||||||
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
|
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.MiraiExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
import net.mamoe.mirai.utils.verbose
|
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
|
lateinit var source: MessageSourceToFriendImpl
|
||||||
(bot.network as QQAndroidBotNetworkHandler).run {
|
(bot.network as QQAndroidBotNetworkHandler).run {
|
||||||
check(
|
check(
|
||||||
MessageSvc.PbSendMsg.createToFriend(
|
MessageSvcPbSendMsg.createToFriend(
|
||||||
bot.asQQAndroidBot().client,
|
bot.asQQAndroidBot().client,
|
||||||
this@sendMessageImpl,
|
this@sendMessageImpl,
|
||||||
event.message
|
event.message
|
||||||
) {
|
) {
|
||||||
source = it
|
source = it
|
||||||
}.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
}.sendAndExpect<MessageSvcPbSendMsg.Response>() is MessageSvcPbSendMsg.Response.SUCCESS
|
||||||
) { "send message failed" }
|
) { "send message failed" }
|
||||||
}
|
}
|
||||||
return MessageReceipt(source, generic, null)
|
return MessageReceipt(source, generic, null)
|
||||||
|
@ -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.ImMsgBody
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
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.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
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||||
|
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ internal class MessageSourceToGroupImpl(
|
|||||||
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
|
||||||
private val sequenceIdDeferred: Deferred<Int?> =
|
private val sequenceIdDeferred: Deferred<Int?> =
|
||||||
coroutineScope.asyncFromEventOrNull<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, Int>(
|
||||||
timeoutMillis = 3000
|
timeoutMillis = 3000
|
||||||
) {
|
) {
|
||||||
if (it.messageRandom == this@MessageSourceToGroupImpl.internalId) {
|
if (it.messageRandom == this@MessageSourceToGroupImpl.internalId) {
|
||||||
|
@ -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.data.proto.MsgSvc
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
|
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.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.list.FriendList
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
|
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..." }
|
logger.info { "Syncing friend message history..." }
|
||||||
withTimeoutOrNull(30000) {
|
withTimeoutOrNull(30000) {
|
||||||
launch { syncFromEvent<MessageSvc.PbGetMsg.GetMsgSuccess, Unit> { Unit } }
|
launch { syncFromEvent<MessageSvcPbGetMsg.GetMsgSuccess, Unit> { Unit } }
|
||||||
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<Packet>()
|
MessageSvcPbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<Packet>()
|
||||||
} ?: error("timeout syncing friend message history")
|
} ?: error("timeout syncing friend message history")
|
||||||
logger.info { "Syncing friend message history: Success" }
|
logger.info { "Syncing friend message history: Success" }
|
||||||
|
|
||||||
|
@ -897,7 +897,7 @@ internal class Submsgtype0x27 {
|
|||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal class MsgBody(
|
internal class SubMsgType0x27MsgBody(
|
||||||
@ProtoId(1) @JvmField val msgModInfos: List<ForwardBody> = listOf()
|
@ProtoId(1) @JvmField val msgModInfos: List<ForwardBody> = listOf()
|
||||||
) : ProtoBuf
|
) : ProtoBuf
|
||||||
|
|
||||||
|
@ -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.TroopManagement
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
|
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.image.LongConn
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.*
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
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.list.ProfileService
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
|
||||||
@ -132,10 +131,10 @@ internal object KnownPacketFactories {
|
|||||||
WtLogin.Login,
|
WtLogin.Login,
|
||||||
StatSvc.Register,
|
StatSvc.Register,
|
||||||
StatSvc.GetOnlineStatus,
|
StatSvc.GetOnlineStatus,
|
||||||
MessageSvc.PbGetMsg,
|
MessageSvcPbGetMsg,
|
||||||
MessageSvc.PushForceOffline,
|
MessageSvcPushForceOffline,
|
||||||
MessageSvc.PbSendMsg,
|
MessageSvcPbSendMsg,
|
||||||
MessageSvc.Del,
|
MessageSvcPbDeleteMsg,
|
||||||
FriendList.GetFriendGroupList,
|
FriendList.GetFriendGroupList,
|
||||||
FriendList.GetTroopListSimplify,
|
FriendList.GetTroopListSimplify,
|
||||||
FriendList.GetTroopMemberList,
|
FriendList.GetTroopMemberList,
|
||||||
@ -157,15 +156,15 @@ internal object KnownPacketFactories {
|
|||||||
)
|
)
|
||||||
|
|
||||||
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
|
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
|
||||||
OnlinePush.PbPushGroupMsg,
|
OnlinePushPbPushGroupMsg,
|
||||||
OnlinePush.ReqPush,
|
OnlinePushReqPush,
|
||||||
OnlinePush.PbPushTransMsg,
|
OnlinePushPbPushTransMsg,
|
||||||
MessageSvc.PushNotify,
|
MessageSvcPushNotify,
|
||||||
ConfigPushSvc.PushReq,
|
ConfigPushSvc.PushReq,
|
||||||
StatSvc.ReqMSFOffline
|
StatSvc.ReqMSFOffline
|
||||||
)
|
)
|
||||||
// SvcReqMSFLoginNotify 自己的其他设备上限
|
// SvcReqMSFLoginNotify 自己的其他设备上限
|
||||||
// MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机
|
// MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机
|
||||||
// OnlinePush.PbC2CMsgSync 电脑发消息给别人, 同步给手机
|
// OnlinePush.PbC2CMsgSync 电脑发消息给别人, 同步给手机
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate") // debugging use
|
@Suppress("MemberVisibilityCanBePrivate") // debugging use
|
||||||
@ -259,21 +258,20 @@ internal object KnownPacketFactories {
|
|||||||
PacketLogger.info { "Handle packet: ${it.commandName}" }
|
PacketLogger.info { "Handle packet: ${it.commandName}" }
|
||||||
it.data.withUse {
|
it.data.withUse {
|
||||||
when (flag2) {
|
when (flag2) {
|
||||||
0, 1 ->
|
0, 1 -> when (it.packetFactory) {
|
||||||
when (it.packetFactory) {
|
is OutgoingPacketFactory<*> -> consumer(
|
||||||
is OutgoingPacketFactory<*> -> consumer(
|
it.packetFactory as OutgoingPacketFactory<T>,
|
||||||
it.packetFactory as OutgoingPacketFactory<T>,
|
it.packetFactory.run { decode(bot, it.data) },
|
||||||
it.packetFactory.run { decode(bot, it.data) },
|
it.packetFactory.commandName,
|
||||||
it.packetFactory.commandName,
|
it.sequenceId
|
||||||
it.sequenceId
|
)
|
||||||
)
|
is IncomingPacketFactory<*> -> consumer(
|
||||||
is IncomingPacketFactory<*> -> consumer(
|
it.packetFactory as IncomingPacketFactory<T>,
|
||||||
it.packetFactory as IncomingPacketFactory<T>,
|
it.packetFactory.run { decode(bot, it.data, it.sequenceId) },
|
||||||
it.packetFactory.run { decode(bot, it.data, it.sequenceId) },
|
it.packetFactory.receivingCommandName,
|
||||||
it.packetFactory.receivingCommandName,
|
it.sequenceId
|
||||||
it.sequenceId
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
2 -> it.data.parseOicqResponse(
|
2 -> it.data.parseOicqResponse(
|
||||||
bot,
|
bot,
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 ?: "")
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
)
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -155,7 +155,7 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() {
|
|||||||
override val messageInternalId: Int,
|
override val messageInternalId: Int,
|
||||||
override val messageTime: Int,
|
override val messageTime: Int,
|
||||||
/**
|
/**
|
||||||
* 撤回操作人, 可能为 [Bot.uin] 或好友的 [User.id]
|
* 撤回操作人, 可能为 [Bot.id] 或好友的 [User.id]
|
||||||
*/
|
*/
|
||||||
val operator: Long
|
val operator: Long
|
||||||
) : MessageRecallEvent(), Packet {
|
) : MessageRecallEvent(), Packet {
|
||||||
|
Loading…
Reference in New Issue
Block a user