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