Commit 1dd4caa0 authored by Him188's avatar Him188

Support recall events, close #88

parent 024bd230
...@@ -19,6 +19,8 @@ import net.mamoe.mirai.data.AddFriendResult ...@@ -19,6 +19,8 @@ import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.FriendInfo import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.GroupInfo import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.MessageRecallEvent
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.message.CustomFaceFromServer import net.mamoe.mirai.qqandroid.message.CustomFaceFromServer
import net.mamoe.mirai.qqandroid.message.NotOnlineImageFromServer import net.mamoe.mirai.qqandroid.message.NotOnlineImageFromServer
...@@ -140,6 +142,14 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -140,6 +142,14 @@ internal abstract class QQAndroidBotBase constructor(
source.time source.time
).sendAndExpect() ).sendAndExpect()
} else { } else {
MessageRecallEvent.GroupRecall(
bot,
source.qqId,
source.id,
source.time.toInt(),
null,
getGroup(source.groupId)
).broadcast()
PbMessageSvc.PbMsgWithDraw.Group( PbMessageSvc.PbMsgWithDraw.Group(
bot.client, bot.client,
source.groupId, source.groupId,
......
...@@ -16,7 +16,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi ...@@ -16,7 +16,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.MultiPacketByIterable
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.events.BotJoinGroupEvent import net.mamoe.mirai.event.events.BotJoinGroupEvent
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
...@@ -107,19 +107,17 @@ internal class MessageSvc { ...@@ -107,19 +107,17 @@ internal class MessageSvc {
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
open class GetMsgSuccess(delegate: List<Packet>) : Response(MsgSvc.SyncFlag.STOP, delegate) { open class GetMsgSuccess(delegate: List<Packet>) : Response(MsgSvc.SyncFlag.STOP, delegate) {
override fun toString(): String { override fun toString(): String = "MessageSvc.PbGetMsg.GetMsgSuccess(messages=<Iterable>))"
return "MessageSvc.PbGetMsg.GetMsgSuccess(messages=List(size=${this.size}))"
}
} }
/** /**
* 不要直接 expect 这个 class. 它可能还没同步完成 * 不要直接 expect 这个 class. 它可能还没同步完成
*/ */
@MiraiInternalAPI @MiraiInternalAPI
open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) : MultiPacket<Packet>(delegate) { open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) :
override fun toString(): String { MultiPacketByIterable<Packet>(delegate) {
return "MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=List(size=${this.size}))" override fun toString(): String =
} "MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
} }
object EmptyResponse : GetMsgSuccess(emptyList()) object EmptyResponse : GetMsgSuccess(emptyList())
......
...@@ -11,9 +11,9 @@ ...@@ -11,9 +11,9 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.io.core.* import io.ktor.utils.io.core.*
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.MultiPacketBySequence
import net.mamoe.mirai.data.NoPacket import net.mamoe.mirai.data.NoPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
...@@ -30,12 +30,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack ...@@ -30,12 +30,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OnlinePushTrans import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OnlinePushTrans
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.IncomingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket 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.buildResponseUniPacket
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.readString import net.mamoe.mirai.utils.io.readString
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
...@@ -60,10 +60,14 @@ internal class OnlinePush { ...@@ -60,10 +60,14 @@ internal class OnlinePush {
if (!bot.firstLoginSucceed) return null if (!bot.firstLoginSucceed) return null
val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer())
val extraInfo: ImMsgBody.ExtraInfo? = pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo val extraInfo: ImMsgBody.ExtraInfo? =
pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo
if (pbPushMsg.msg.msgHead.fromUin == bot.uin) { if (pbPushMsg.msg.msgHead.fromUin == bot.uin) {
return SendGroupMessageReceipt(pbPushMsg.msg.msgBody.richText.attr!!.random, pbPushMsg.msg.msgHead.msgSeq) return SendGroupMessageReceipt(
pbPushMsg.msg.msgBody.richText.attr!!.random,
pbPushMsg.msg.msgHead.msgSeq
)
} }
val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode)
...@@ -106,13 +110,22 @@ internal class OnlinePush { ...@@ -106,13 +110,22 @@ internal class OnlinePush {
val group = bot.getGroupByUin(content.fromUin) as GroupImpl val group = bot.getGroupByUin(content.fromUin) as GroupImpl
if (var5 == 0L && this.remaining == 1L) {//管理员变更 if (var5 == 0L && this.remaining == 1L) {//管理员变更
val newPermission = if (this.readByte().toInt() == 1) MemberPermission.ADMINISTRATOR else MemberPermission.MEMBER val newPermission =
if (this.readByte().toInt() == 1) MemberPermission.ADMINISTRATOR else MemberPermission.MEMBER
return if (target == bot.uin) { return if (target == bot.uin) {
BotGroupPermissionChangeEvent(group, group.botPermission.also { group.botPermission = newPermission }, newPermission) BotGroupPermissionChangeEvent(
group,
group.botPermission.also { group.botPermission = newPermission },
newPermission
)
} else { } else {
val member = group[target] as MemberImpl val member = group[target] as MemberImpl
MemberPermissionChangeEvent(member, member.permission.also { member.permission = newPermission }, newPermission) MemberPermissionChangeEvent(
member,
member.permission.also { member.permission = newPermission },
newPermission
)
} }
} }
} }
...@@ -176,144 +189,218 @@ internal class OnlinePush { ...@@ -176,144 +189,218 @@ internal class OnlinePush {
val reqPushMsg = decodeUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req") val reqPushMsg = decodeUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req")
@Suppress("USELESS_CAST") // 不要信任 kotlin 类型推断 @Suppress("USELESS_CAST") // 不要信任 kotlin 类型推断
val packets: List<Packet> = reqPushMsg.vMsgInfos.mapNotNull { msgInfo: MsgInfo -> val packets: Sequence<Packet> =
msgInfo.vMsg!!.read { reqPushMsg.vMsgInfos.asSequence().flatMap<MsgInfo, Packet> { msgInfo: MsgInfo ->
when { msgInfo.vMsg!!.read {
msgInfo.shMsgType.toInt() == 732 -> { when (msgInfo.shMsgType.toInt()) {
val group = bot.getGroup(this.readUInt().toLong()) 732 -> {
group as GroupImpl val group = bot.getGroup(this.readUInt().toLong())
group as GroupImpl
when (val internalType = this.readShort().toInt()) {
3073 -> { // mute
val operatorUin = this.readUInt().toLong()
if (operatorUin == bot.uin) {
return@mapNotNull null
}
val operator = group[operatorUin]
this.readUInt().toLong() // time
this.discardExact(2)
val target = this.readUInt().toLong()
val time = this.readInt()
if (target == 0L) { when (val internalType = this.readByte().toInt().also { this.discardExact(1) }) {
if (time == 0) { 0x0c -> { // mute
return@mapNotNull GroupMuteAllEvent( val operatorUin = this.readUInt().toLong()
origin = group.isMuteAll.also { group._muteAll = false }, if (operatorUin == bot.uin) {
new = false, return@flatMap sequenceOf<Packet>()
operator = operator,
group = group
) as Packet
} else {
return@mapNotNull GroupMuteAllEvent(
origin = group.isMuteAll.also { group._muteAll = true },
new = true,
operator = operator,
group = group
) as Packet
} }
} else { val operator = group[operatorUin]
if (target == bot.uin) { this.readUInt().toLong() // time
if (group._botMuteTimestamp != time) { this.discardExact(2)
if (time == 0) { val target = this.readUInt().toLong()
group._botMuteTimestamp = 0 val time = this.readInt()
return@mapNotNull BotUnmuteEvent(operator) as Packet
} else { if (target == 0L) {
group._botMuteTimestamp = time if (time == 0) {
return@mapNotNull BotMuteEvent(durationSeconds = time, operator = operator) as Packet return@flatMap sequenceOf(
} GroupMuteAllEvent(
origin = group.isMuteAll.also { group._muteAll = false },
new = false,
operator = operator,
group = group
) as Packet
)
} else { } else {
return@mapNotNull null return@flatMap sequenceOf(
GroupMuteAllEvent(
origin = group.isMuteAll.also { group._muteAll = true },
new = true,
operator = operator,
group = group
) as Packet
)
} }
} else { } else {
val member = group[target] if (target == bot.uin) {
member as MemberImpl if (group._botMuteTimestamp != time) {
if (member._muteTimestamp != time) { if (time == 0) {
if (time == 0) { group._botMuteTimestamp = 0
member._muteTimestamp = 0 return@flatMap sequenceOf(BotUnmuteEvent(operator) as Packet)
return@mapNotNull MemberUnmuteEvent(member, operator) as Packet } else {
group._botMuteTimestamp = time
return@flatMap sequenceOf(
BotMuteEvent(
durationSeconds = time,
operator = operator
) as Packet
)
}
} else { } else {
member._muteTimestamp = time return@flatMap sequenceOf()
return@mapNotNull MemberMuteEvent(member, time, operator) as Packet
} }
} else { } else {
return@mapNotNull null val member = group[target]
member as MemberImpl
if (member._muteTimestamp != time) {
if (time == 0) {
member._muteTimestamp = 0
return@flatMap sequenceOf(
MemberUnmuteEvent(
member,
operator
) as Packet
)
} else {
member._muteTimestamp = time
return@flatMap sequenceOf(
MemberMuteEvent(
member,
time,
operator
) as Packet
)
}
} else {
return@flatMap sequenceOf()
}
} }
} }
} }
} 0x0e -> {
3585 -> { // 匿名
// 匿名 val operator = group[this.readUInt().toLong()]
val operator = group[this.readUInt().toLong()] val switch = this.readInt() == 0
val switch = this.readInt() == 0 return@flatMap sequenceOf(
return@mapNotNull GroupAllowAnonymousChatEvent( GroupAllowAnonymousChatEvent(
origin = group.isAnonymousChatEnabled.also { group._anonymousChat = switch }, origin = group.isAnonymousChatEnabled.also {
new = switch, group._anonymousChat = switch
operator = operator, },
group = group new = switch,
) operator = operator,
} group = group
4096 -> { )
val dataBytes = this.readBytes(26)
val message = this.readString(this.readByte().toInt())
// println(dataBytes.toUHexString())
if (dataBytes[0].toInt() != 59) {
return@mapNotNull GroupNameChangeEvent(
origin = group.name.also { group._name = message },
new = message,
group = group,
isByBot = false
) )
} else { }
//println(message + ":" + dataBytes.toUHexString()) 0x10 -> {
when (message) { val dataBytes = this.readBytes(26)
"管理员已关闭群聊坦白说" -> { val message = this.readString(this.readByte().toInt())
return@mapNotNull GroupAllowConfessTalkEvent( // println(dataBytes.toUHexString())
origin = group.isConfessTalkEnabled.also { group._confessTalk = false },
new = false, if (dataBytes[0].toInt() != 59) {
group = group, return@flatMap sequenceOf(
isByBot = false GroupNameChangeEvent(
) origin = group.name.also { group._name = message },
} new = message,
"管理员已开启群聊坦白说" -> {
return@mapNotNull GroupAllowConfessTalkEvent(
origin = group.isConfessTalkEnabled.also { group._confessTalk = true },
new = true,
group = group, group = group,
isByBot = false isByBot = false
) )
)
} else {
//println(message + ":" + dataBytes.toUHexString())
when (message) {
"管理员已关闭群聊坦白说" -> {
return@flatMap sequenceOf(
GroupAllowConfessTalkEvent(
origin = group.isConfessTalkEnabled.also {
group._confessTalk = false
},
new = false,
group = group,
isByBot = false
)
)
}
"管理员已开启群聊坦白说" -> {
return@flatMap sequenceOf(
GroupAllowConfessTalkEvent(
origin = group.isConfessTalkEnabled.also {
group._confessTalk = true
},
new = true,
group = group,
isByBot = false
)
)
}
else -> {
bot.network.logger.debug { "Unknown server messages $message" }
return@flatMap sequenceOf()
}
} }
else -> { }
bot.network.logger.debug { "Unknown server messages $message" } }
return@mapNotNull null 0x11 -> {
discard(1)
val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer())
proto.optMsgRecall?.let { recallReminder ->
return@flatMap recallReminder.recalledMsgList.asSequence()
.mapNotNull { meta ->
if (meta.authorUin == bot.uin) {
null
} else MessageRecallEvent.GroupRecall(
bot,
meta.authorUin,
meta.seq.toLong().shl(32) or meta.msgRandom.toLong(),
meta.time,
group.get(recallReminder.uin),
group
)
}
/*
optMsgRecall=MessageRecallReminder#1345636186 {
groupType=0x00000000(0)
nickname=<Empty ByteArray>
opType=0x00000000(0)
recalledMsgList=[MessageMeta#1828757853 {
authorUin=0x000000003E033FA2(1040400290)
msgFlag=0x00000000(0)
msgRandom=0x509119DC(1351686620)
msgType=0x00000000(0)
seq=0x0000EB4B(60235)
time=0x5E50C4EC(1582351596)
}]
reminderContent=<Empty ByteArray>
uin=0x000000003E033FA2(1040400290)
userdef=08 00 12 0A 08 CB D6 03 10 00 18 01 20 00
} }
*/
} }
return@flatMap sequenceOf()
}
// 4352 -> {
// println(msgInfo.contentToString())
// println(msgInfo.vMsg.toUHexString())
// }
else -> {
bot.network.logger.debug { "unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " " }
return@flatMap sequenceOf()
} }
}
// 4352 -> {
// println(msgInfo.contentToString())
// println(msgInfo.vMsg.toUHexString())
// }
else -> {
bot.network.logger.debug { "unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " " }
return@mapNotNull null
} }
} }
} 528 -> {
msgInfo.shMsgType.toInt() == 528 -> { bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" }
bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" } // val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
// val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer()) // println(content.contentToString())
// println(content.contentToString()) return@flatMap sequenceOf()
return@mapNotNull null }
} else -> {
else -> { bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" }
bot.network.logger.debug { "unknown shtype ${msgInfo.shMsgType.toInt()}" } return@flatMap sequenceOf()
return@mapNotNull null }
} }
} }
} }
} return MultiPacketBySequence(packets)
return MultiPacket(packets)
} }
override suspend fun QQAndroidBot.handle(packet: Packet, sequenceId: Int): OutgoingPacket? { override suspend fun QQAndroidBot.handle(packet: Packet, sequenceId: Int): OutgoingPacket? {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment